WIP : IEEE 802.1x KaY support

Change-Id: I16ac0af06e33950b1585728bdd41e9091853e413
diff --git a/pom.xml b/pom.xml
index 515bd24..339e615 100644
--- a/pom.xml
+++ b/pom.xml
@@ -22,8 +22,8 @@
     <parent>
         <groupId>org.onosproject</groupId>
         <artifactId>onos-dependencies</artifactId>
-        <version>1.10.9</version>
-        <relativePath></relativePath>
+        <version>1.12.1-SNAPSHOT</version>
+        <relativePath>../onos/lib/pom.xml</relativePath>
     </parent>
 
     <groupId>org.opencord</groupId>
@@ -35,7 +35,7 @@
 
     <properties>
         <onos.app.name>org.opencord.aaa</onos.app.name>
-        <onos.version>1.10.9</onos.version>
+        <onos.version>1.12.1-SNAPSHOT</onos.version>
         <onos.app.title>AAA App</onos.app.title>
         <onos.app.category>Security</onos.app.category>
         <onos.app.url>http://opencord.org</onos.app.url>
@@ -44,8 +44,8 @@
               org.onosproject.olt,
               org.opencord.sadis
         </onos.app.requires>
-        <sadis.api.version>2.0.0</sadis.api.version>
-        <olt.api.version>1.3.1</olt.api.version>
+        <sadis.api.version>2.0.1-SNAPSHOT</sadis.api.version>
+        <olt.api.version>1.3.2-SNAPSHOT</olt.api.version>
     </properties>
 
     <dependencies>
@@ -86,6 +86,28 @@
             <artifactId>onlab-junit</artifactId>
             <version>${onos.version}</version>
         </dependency>
+    
+         <dependency>
+              <groupId>org.onosproject</groupId>
+              <artifactId>onos-protocols-netconf-api</artifactId>
+              <version>1.8.5-SNAPSHOT</version>
+          </dependency>
+          <dependency>
+              <groupId>org.onosproject</groupId>
+              <artifactId>onos-protocols-netconf-ctl</artifactId>
+              <version>1.8.5-SNAPSHOT</version>
+          </dependency>
+
+          <dependency>
+              <groupId>org.apache.karaf.shell</groupId>
+              <artifactId>org.apache.karaf.shell.console</artifactId>
+         </dependency>
+
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onos-core-common</artifactId>
+            <version>${onos.version}</version>
+        </dependency>
 
     </dependencies>
 
diff --git a/src/main/java/org/opencord/aaa/AaaConfig.java b/src/main/java/org/opencord/aaa/AaaConfig.java
index 8ffdf00..99d1589 100644
--- a/src/main/java/org/opencord/aaa/AaaConfig.java
+++ b/src/main/java/org/opencord/aaa/AaaConfig.java
@@ -15,9 +15,6 @@
  */
 package org.opencord.aaa;
 
-import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.node.ArrayNode;
-
 import com.google.common.collect.ImmutableSet;
 
 import org.onosproject.core.ApplicationId;
@@ -54,7 +51,8 @@
     private static final String PACKET_CUSTOMIZER = "packetCustomizer";
 
     // RADIUS server IP address
-    protected static final String DEFAULT_RADIUS_IP = "10.128.10.4";
+    //protected static final String DEFAULT_RADIUS_IP = "10.128.10.4";
+    protected static final String DEFAULT_RADIUS_IP = "192.168.1.14";
 
     // RADIUS MAC address
     protected static final String DEFAULT_RADIUS_MAC = "00:00:00:00:01:10";
@@ -66,7 +64,8 @@
     protected static final String DEFAULT_NAS_MAC = "00:00:00:00:10:01";
 
     // RADIUS server shared secret
-    protected static final String DEFAULT_RADIUS_SECRET = "ONOSecret";
+    //protected static final String DEFAULT_RADIUS_SECRET = "ONOSecret";
+    protected static final String DEFAULT_RADIUS_SECRET = "karaf";
 
     // Radius Server UDP Port Number
     protected static final String DEFAULT_RADIUS_SERVER_PORT = "1812";
@@ -83,7 +82,6 @@
     // Packet Customizer Default value
     protected static final String DEFAULT_PACKET_CUSTOMIZER = "default";
 
-
     /**
      * Gets the value of a string property, protecting for an empty
      * JSON object.
@@ -271,7 +269,7 @@
         if (!object.has(RADIUS_SERVER_CONNECTPOINTS)) {
             return ImmutableSet.of();
         }
-
+       /* Following portion to be rewritten with out Guava ImmutableSet...
         ImmutableSet.Builder<ConnectPoint> builder = ImmutableSet.builder();
         ArrayNode arrayNode = (ArrayNode) object.path(RADIUS_SERVER_CONNECTPOINTS);
         for (JsonNode jsonNode : arrayNode) {
@@ -285,6 +283,7 @@
                 return null;
             }
         }
-        return builder.build();
+        return builder.build();*/
+        return new HashSet<ConnectPoint>();
     }
 }
diff --git a/src/main/java/org/opencord/aaa/AaaManager.java b/src/main/java/org/opencord/aaa/AaaManager.java
index 799064a..972a072 100755
--- a/src/main/java/org/opencord/aaa/AaaManager.java
+++ b/src/main/java/org/opencord/aaa/AaaManager.java
@@ -19,8 +19,10 @@
 import org.apache.felix.scr.annotations.Deactivate;
 import org.apache.felix.scr.annotations.Reference;
 import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.onlab.packet.DeserializationException;
 import org.onlab.packet.EAP;
 import org.onlab.packet.EAPOL;
+import org.onlab.packet.EAPOL_MKPDU;
 import org.onlab.packet.EthType;
 import org.onlab.packet.Ethernet;
 import org.onlab.packet.MacAddress;
@@ -33,6 +35,7 @@
 import org.onosproject.net.AnnotationKeys;
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.DeviceId;
+import org.onosproject.net.Port;
 import org.onosproject.net.PortNumber;
 import org.onosproject.net.config.ConfigFactory;
 import org.onosproject.net.config.NetworkConfigEvent;
@@ -43,6 +46,8 @@
 import org.onosproject.net.device.DeviceService;
 import org.onosproject.net.flow.DefaultTrafficTreatment;
 import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.intf.Interface;
+import org.onosproject.net.intf.InterfaceService;
 import org.onosproject.net.packet.DefaultOutboundPacket;
 import org.onosproject.net.packet.InboundPacket;
 import org.onosproject.net.packet.OutboundPacket;
@@ -57,11 +62,18 @@
 
 import java.net.InetAddress;
 import java.nio.ByteBuffer;
+import java.util.AbstractMap;
 import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
 
 import static org.onosproject.net.config.basics.SubjectFactories.APP_SUBJECT_FACTORY;
 import static org.slf4j.LoggerFactory.getLogger;
 
+import org.onlab.packet.EAPOL_MKPDU_BasicParameterSet.SCI;
+
 /**
  * AAA application for ONOS.
  */
@@ -97,6 +109,9 @@
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected AccessDeviceService accessDeviceService;
 
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected InterfaceService interfaceService;
+
     private final DeviceListener deviceListener = new InternalDeviceListener();
 
     // NAS IP address
@@ -143,20 +158,51 @@
     // latest configuration
     AaaConfig newCfg;
 
+    //MACSec verion
+    private String version;
+
+    //MACSec capability
+    private String capability;
+
+    //Secure Connectivity Association Key NameK
+    private String mkaCkn;
+
+    //Secure Connectivity Association Key
+    private String mkaCak;
+
+    //Default Netconf Device ID
+    private DeviceId defaultNetconfDeviceId;
+
     // Configuration properties factory
     private final ConfigFactory factory =
             new ConfigFactory<ApplicationId, AaaConfig>(APP_SUBJECT_FACTORY,
-                                                         AaaConfig.class,
-                                                         "AAA") {
+                    AaaConfig.class,
+                    "AAA") {
                 @Override
                 public AaaConfig createConfig() {
                     return new AaaConfig();
                 }
             };
 
+    // Configuration properties factory with MACSec
+    private final ConfigFactory factoryWithMacSec =
+            new ConfigFactory<ApplicationId, MacSecConfig>(APP_SUBJECT_FACTORY,
+                    MacSecConfig.class,
+                    "MACSEC") {
+                @Override
+                public MacSecConfig createConfig() {
+                    return new MacSecConfig();
+                }
+            };
+
     // Listener for config changes
     private final InternalConfigListener cfgListener = new InternalConfigListener();
 
+    // MACSec KaY store and SecY drivers
+    private Map<DeviceId, Map.Entry<MacSecKaY, MacSecSecY>>
+            mkaStore = new ConcurrentHashMap<>();
+
+
     /**
      * Builds an EAPOL packet based on the given parameters.
      *
@@ -197,6 +243,12 @@
         appId = coreService.registerApplication(APP_NAME);
         customInfo = new CustomizationInfo(subsService, deviceService);
         cfgListener.reconfigureNetwork(netCfgService.getConfig(appId, AaaConfig.class));
+
+       /* Enable MACSec configuration Support */
+        netCfgService.registerConfigFactory(factoryWithMacSec);
+        cfgListener.reconfigureNetworkWithMacSec(netCfgService.getConfig(appId, MacSecConfig.class));
+        deviceService.addListener(new InternalDeviceListener());
+
         configureRadiusCommunication();
 
         // register our event handler
@@ -227,6 +279,7 @@
         StateMachine.destroyMaps();
         impl.deactivate();
         deviceService.removeListener(deviceListener);
+        macSecThreadPool.shutdown();
         log.info("Stopped");
     }
 
@@ -280,57 +333,72 @@
 
         switch (radiusPacket.getCode()) {
             case RADIUS.RADIUS_CODE_ACCESS_CHALLENGE:
-                RADIUSAttribute radiusAttrState = radiusPacket.getAttribute(RADIUSAttribute.RADIUS_ATTR_STATE);
-                byte[] challengeState = null;
-                if (radiusAttrState != null) {
-                    challengeState = radiusAttrState.getValue();
+                try {
+                    RADIUSAttribute radiusAttrState = radiusPacket.getAttribute(RADIUSAttribute.RADIUS_ATTR_STATE);
+                    byte[] challengeState = null;
+                    if (radiusAttrState != null) {
+                        challengeState = radiusAttrState.getValue();
+                    }
+
+                    eapPayload = radiusPacket.decapsulateMessage();
+
+                    stateMachine.setChallengeInfo(eapPayload.getIdentifier(), challengeState);
+                    eth = buildEapolResponse(stateMachine.supplicantAddress(),
+                            MacAddress.valueOf(nasMacAddress),
+                            stateMachine.vlanId(),
+                            EAPOL.EAPOL_PACKET,
+                            eapPayload, stateMachine.priorityCode());
+                    sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint());
+                } catch (DeserializationException e) {
+                    e.printStackTrace();
                 }
-                eapPayload = radiusPacket.decapsulateMessage();
-                stateMachine.setChallengeInfo(eapPayload.getIdentifier(), challengeState);
-                eth = buildEapolResponse(stateMachine.supplicantAddress(),
-                        MacAddress.valueOf(nasMacAddress),
-                        stateMachine.vlanId(),
-                        EAPOL.EAPOL_PACKET,
-                        eapPayload, stateMachine.priorityCode());
-                sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint());
                 break;
             case RADIUS.RADIUS_CODE_ACCESS_ACCEPT:
-                //send an EAPOL - Success to the supplicant.
-                byte[] eapMessageSuccess =
-                        radiusPacket.getAttribute(RADIUSAttribute.RADIUS_ATTR_EAP_MESSAGE).getValue();
-                eapPayload = new EAP();
-                eapPayload = (EAP) eapPayload.deserialize(eapMessageSuccess, 0, eapMessageSuccess.length);
-                eth = buildEapolResponse(stateMachine.supplicantAddress(),
-                        MacAddress.valueOf(nasMacAddress),
-                        stateMachine.vlanId(),
-                        EAPOL.EAPOL_PACKET,
-                        eapPayload, stateMachine.priorityCode());
-                sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint());
+                try {
+                    //send an EAPOL - Success to the supplicant.
+                    byte[] eapMessageSuccess =
+                            radiusPacket.getAttribute(RADIUSAttribute.RADIUS_ATTR_EAP_MESSAGE).getValue();
+                    eapPayload = new EAP();
+                    //eapPayload = (EAP) eapPayload.deserialize(eapMessageSuccess, 0, eapMessageSuccess.length);
+                    eapPayload = eapPayload.deserializer().deserialize(eapMessageSuccess, 0, eapMessageSuccess.length);
+                    eth = buildEapolResponse(stateMachine.supplicantAddress(),
+                            MacAddress.valueOf(nasMacAddress),
+                            stateMachine.vlanId(),
+                            EAPOL.EAPOL_PACKET,
+                            eapPayload, stateMachine.priorityCode());
+                    sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint());
 
-                stateMachine.authorizeAccess();
-
+                    stateMachine.authorizeAccess();
+                } catch (DeserializationException e) {
+                    log.error("Error in processing RADIUS message {}", e);
+                }
                 break;
             case RADIUS.RADIUS_CODE_ACCESS_REJECT:
-                //send an EAPOL - Failure to the supplicant.
-                byte[] eapMessageFailure;
-                eapPayload = new EAP();
-                RADIUSAttribute radiusAttrEap = radiusPacket.getAttribute(RADIUSAttribute.RADIUS_ATTR_EAP_MESSAGE);
-                if (radiusAttrEap == null) {
-                    eapPayload.setCode(EAP.FAILURE);
-                    eapPayload.setIdentifier(stateMachine.challengeIdentifier());
-                    eapPayload.setLength(EAP.EAP_HDR_LEN_SUC_FAIL);
-                } else {
-                    eapMessageFailure = radiusAttrEap.getValue();
-                    eapPayload = (EAP) eapPayload.deserialize(eapMessageFailure, 0, eapMessageFailure.length);
+                try {
+                    //send an EAPOL - Failure to the supplicant.
+                    byte[] eapMessageFailure;
+                    eapPayload = new EAP();
+                    RADIUSAttribute radiusAttrEap = radiusPacket.getAttribute(RADIUSAttribute.RADIUS_ATTR_EAP_MESSAGE);
+                    if (radiusAttrEap == null) {
+                        eapPayload.setCode(EAP.FAILURE);
+                        eapPayload.setIdentifier(stateMachine.challengeIdentifier());
+                        eapPayload.setLength(EAP.EAP_HDR_LEN_SUC_FAIL);
+                    } else {
+                        eapMessageFailure = radiusAttrEap.getValue();
+                        //eapPayload = (EAP) eapPayload.deserialize(eapMessageFailure, 0, eapMessageFailure.length);
+                        eapPayload = (EAP) eapPayload.deserializer()
+                                .deserialize(eapMessageFailure, 0, eapMessageFailure.length);
+                    }
+                    eth = buildEapolResponse(stateMachine.supplicantAddress(),
+                            MacAddress.valueOf(nasMacAddress),
+                            stateMachine.vlanId(),
+                            EAPOL.EAPOL_PACKET,
+                            eapPayload, stateMachine.priorityCode());
+                    sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint());
+                    stateMachine.denyAccess();
+                } catch (DeserializationException e) {
+                    log.error("Error in processing RADIUS REJECT {}", e);
                 }
-                eth = buildEapolResponse(stateMachine.supplicantAddress(),
-                        MacAddress.valueOf(nasMacAddress),
-                        stateMachine.vlanId(),
-                        EAPOL.EAPOL_PACKET,
-                        eapPayload, stateMachine.priorityCode());
-                sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint());
-                stateMachine.denyAccess();
-
                 break;
             default:
                 log.warn("Unknown RADIUS message received with code: {}", radiusPacket.getCode());
@@ -346,7 +414,7 @@
     private void sendPacketToSupplicant(Ethernet ethernetPkt, ConnectPoint connectPoint) {
         TrafficTreatment treatment = DefaultTrafficTreatment.builder().setOutput(connectPoint.port()).build();
         OutboundPacket packet = new DefaultOutboundPacket(connectPoint.deviceId(),
-                                                          treatment, ByteBuffer.wrap(ethernetPkt.serialize()));
+                treatment, ByteBuffer.wrap(ethernetPkt.serialize()));
         packetService.emit(packet);
     }
 
@@ -385,20 +453,20 @@
          * Creates and initializes common fields of a RADIUS packet.
          *
          * @param stateMachine state machine for the request
-         * @param eapPacket  EAP packet
+         * @param eapPacket    EAP packet
          * @return RADIUS packet
          */
         private RADIUS getRadiusPayload(StateMachine stateMachine, byte identifier, EAP eapPacket) {
             RADIUS radiusPayload =
                     new RADIUS(RADIUS.RADIUS_CODE_ACCESS_REQUEST,
-                               eapPacket.getIdentifier());
+                            eapPacket.getIdentifier());
 
             // set Request Authenticator in StateMachine
             stateMachine.setRequestAuthenticator(radiusPayload.generateAuthCode());
 
             radiusPayload.setIdentifier(identifier);
             radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_USERNAME,
-                                       stateMachine.username());
+                    stateMachine.username());
 
             radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_NAS_IP,
                     AaaManager.this.nasIpAddress.getAddress());
@@ -450,11 +518,11 @@
                     //send an EAP Request/Identify to the supplicant
                     EAP eapPayload = new EAP(EAP.REQUEST, stateMachine.identifier(), EAP.ATTR_IDENTITY, null);
                     if (ethPkt.getVlanID() != Ethernet.VLAN_UNTAGGED) {
-                       stateMachine.setPriorityCode(ethPkt.getPriorityCode());
+                        stateMachine.setPriorityCode(ethPkt.getPriorityCode());
                     }
                     Ethernet eth = buildEapolResponse(srcMac, MacAddress.valueOf(nasMacAddress),
-                                                      ethPkt.getVlanID(), EAPOL.EAPOL_PACKET,
-                                                      eapPayload, stateMachine.priorityCode());
+                            ethPkt.getVlanID(), EAPOL.EAPOL_PACKET,
+                            eapPayload, stateMachine.priorityCode());
                     stateMachine.setSupplicantAddress(srcMac);
                     stateMachine.setVlanId(ethPkt.getVlanID());
 
@@ -496,8 +564,8 @@
                                 //send the RADIUS challenge response
                                 radiusPayload =
                                         getRadiusPayload(stateMachine,
-                                                         stateMachine.identifier(),
-                                                         eapPacket);
+                                                stateMachine.identifier(),
+                                                eapPacket);
                                 radiusPayload = pktCustomizer.customizePacket(radiusPayload, inPacket);
 
                                 if (stateMachine.challengeState() != null) {
@@ -531,6 +599,78 @@
                             return;
                     }
                     break;
+                case EAPOL.EAPOL_MKA:
+
+                    // Received MACSec PDU from supplicant.
+
+                    /* For version 1.0 Logon Process (IEEE802.1X 2010 Clause 12.5) starts here.
+                     * Currently only authenticated & secure connections are supported.
+                     * */
+
+                    /* TODO : Ensure AAA authentication is success. */
+
+                    /* Perform Sanity Check */
+                    EAPOL_MKPDU mkpdu = (EAPOL_MKPDU) eapol.getPayload();
+
+                    /* TODO :  Sanity checking
+                    if(!MKPDUSanityCheck(mkpdu)) {
+                        log.warn("Sanity failed for MKPDU from {}, dropping it.", srcMac);
+                        return;
+                    }*/
+
+                    // Load participant details(key <device><port>) and let it process packet.
+                    Map.Entry<MacSecKaY, MacSecSecY> mka = mkaStore.getOrDefault(deviceId, null);
+                    if (mka == null) {
+                        log.error("EAPOL-MKA received from undetected device {}. Ignoring.", deviceId);
+                        return;
+                    }
+                    MacSecKaY kaY = mka.getKey();
+                    MkaParticipant participant = kaY.lookupParticipantByID(sessionId);
+                    if (participant != null) {
+                        participant.receive(mkpdu);
+                    } else {
+                        // New MKA actor; prepare default resources.
+
+                        /* TODO : Locate CAK, CKN for <device>:<port>.
+                         * Currently global CAK & CKN. */
+                        byte[] cak = MkaKeyUtils.hexStringToByteArray(mkaCak);
+                        byte[] ckn = MkaKeyUtils.hexStringToByteArray(mkaCkn);
+
+                        // Initialize participant details
+                        Port port = deviceService.getPort(deviceId, portNumber);
+                        MacAddress portMac = null;
+                        Optional<MacAddress> macAddress = interfaceService.getInterfacesByPort(inPacket.receivedFrom())
+                                .stream()
+                                .map(Interface::mac).findFirst();
+                        if (macAddress.isPresent()) {
+                            portMac = macAddress.get();
+                        } else {
+                            log.warn("Fetching EAPOL-MKA ingress port MAC from annotations.");
+                            portMac = MacAddress.valueOf(port.annotations().value("portMac"));
+                        }
+                        participant = new MkaParticipant(stateMachine, packetService,
+                                macSecThreadPool,
+                                kaY, mka.getValue(), cak, ckn,
+                                new SCI(portMac.toBytes(), (short) (port.number().toLong())),
+                                deviceId, port);
+
+                        // Initialize CP state machine.
+                        CpStateMachine cpSM = new CpStateMachine(participant, mka.getValue(),
+                                kaY.defaultCipherSuit(), participant.latestKI(),
+                                participant.latestAN(), macSecThreadPool);
+
+                        // Update per port details in store
+                        kaY.addPortDetails(stateMachine.sessionId(), participant, cpSM);
+
+                        // Handover EAPOL_MKPDU to participant to handle it.
+                        participant.receive(mkpdu);
+
+                        // Start participant Hello timer & CP state machine.
+                        participant.startTicking();
+                        cpSM.startStateMachine();
+                    }
+
+                    break;
                 default:
                     log.trace("Skipping EAPOL message {}", eapol.getEapolType());
             }
@@ -595,6 +735,37 @@
             }
         }
 
+        /**
+         * Reconfigures the AAA application according to the
+         * configuration parameters passed.
+         *
+         * @param cfg configuration object
+         **/
+        private void reconfigureNetworkWithMacSec(MacSecConfig cfg) {
+            MacSecConfig newCfg;
+            if (cfg == null) {
+                newCfg = new MacSecConfig();
+            } else {
+                newCfg = cfg;
+            }
+            if (newCfg.version() != null) {
+                version = newCfg.version();
+            }
+            if (newCfg.capability() != null) {
+                capability = newCfg.capability();
+            }
+            if (newCfg.ckn() != null) {
+                mkaCkn = newCfg.ckn();
+            }
+            if (newCfg.cak() != null) {
+                mkaCak = newCfg.cak();
+            }
+            if (newCfg.netConfDeviceId() != null) {
+                defaultNetconfDeviceId = newCfg.netConfDeviceId();
+            }
+        }
+
+
         @Override
         public void event(NetworkConfigEvent event) {
 
@@ -604,6 +775,9 @@
 
                 AaaConfig cfg = netCfgService.getConfig(appId, AaaConfig.class);
                 reconfigureNetwork(cfg);
+                //Include MACSec
+                MacSecConfig cfgMacSec = netCfgService.getConfig(appId, MacSecConfig.class);
+                reconfigureNetworkWithMacSec(cfgMacSec);
 
                 log.info("Reconfigured");
             }
@@ -613,10 +787,9 @@
     private class InternalDeviceListener implements DeviceListener {
         @Override
         public void event(DeviceEvent event) {
-
+            DeviceId devId = event.subject().id();
             switch (event.type()) {
                 case PORT_REMOVED:
-                    DeviceId devId = event.subject().id();
                     PortNumber portNumber = event.port().number();
                     String sessionId = devId.toString() + portNumber.toString();
 
@@ -626,10 +799,42 @@
                         StateMachine.deleteStateMachineMapping(removed);
                     }
 
+                case DEVICE_ADDED:
+                case DEVICE_AVAILABILITY_CHANGED:
+                    // Derive paired netconf device id from openflow device id
+                    DeviceId netconfId = getNetconfPairId(devId);
+                    if (netconfId == null) {
+                        netconfId = defaultNetconfDeviceId;
+                    // netconfId = DeviceId.deviceId("netconf:192.168.1.2:2022");
+                    }
+
+                    // Update store with KaY and SecY device details.
+                    MacSecSecY secY = new MacSecSecY(netconfId);
+                    MacSecKaY kaY = new MacSecKaY(secY);
+                    mkaStore.put(devId,
+                            new AbstractMap.SimpleEntry<MacSecKaY, MacSecSecY>(kaY, secY));
                     break;
+
+                case DEVICE_REMOVED:
+                    mkaStore.remove(devId);
+                    break;
+
                 default:
                     return;
             }
         }
+
+        // Openflow device to netconf device mapping logic for MACSec SecY switches
+        private DeviceId getNetconfPairId(DeviceId deviceID) {
+            /* TODO : Finalize and implement logic */
+            return null;
+        }
     }
+
+    // Device listener for MACSec capability loading.
+    // MACSec Thread Pool. TODO: Pool Size Fixing.
+    private static final int MACSEC_THREAD_POOL_SIZE = 10;
+    private ScheduledExecutorService macSecThreadPool =
+            Executors.newScheduledThreadPool(MACSEC_THREAD_POOL_SIZE);
+
 }
diff --git a/src/main/java/org/opencord/aaa/AescMac.java b/src/main/java/org/opencord/aaa/AescMac.java
new file mode 100644
index 0000000..65a5dda
--- /dev/null
+++ b/src/main/java/org/opencord/aaa/AescMac.java
@@ -0,0 +1,302 @@
+/*
+ * Copyright 2018-present. * Open Networking Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.opencord.aaa;
+
+import javax.crypto.Cipher;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+import java.io.ByteArrayOutputStream;
+
+/**
+ * AES-CMAC Algorithm. RFC 4493.
+ * Cipher-based Message Authentication Code(CMAC),
+ * a keyed hash function based on a symmetric key block cipher: Advanced Encryption Standard(AES).
+ * Reference: https://github.com/E3V3A/androsmex/blob/master/src/de/tsenger/androsmex/tools/AESCMac.java
+ */
+public class AescMac {
+
+    private static byte[] constZero = MkaKeyUtils.hexStringToByteArray("00000000000000000000000000000000");
+    private static byte[] constRb = MkaKeyUtils.hexStringToByteArray("00000000000000000000000000000087");
+    private byte[] key;
+    private ByteArrayOutputStream catInput;
+
+    /**
+     * AES 128-bit encryption using JDK built-in classes.
+     */
+    private static class AesCrypto {
+
+        static Cipher aesEcb = null;
+
+        static {
+            try {
+                aesEcb = Cipher.getInstance("AES/ECB/NOPADDING");
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        }
+
+        /**
+         * Encrypt AES 128 bit key.
+         * @param zeroConst - Zero-constant
+         * @param key - Input key value
+         * @return Encrypted Key
+         */
+        private static byte[] encryptAES128(byte[] zeroConst, byte[] key) {
+            SecretKey aesKey = new SecretKeySpec(key, 0, 16, "AES");
+            try {
+                synchronized (aesEcb) {
+                    aesEcb.init(Cipher.ENCRYPT_MODE, aesKey);
+                    return aesEcb.doFinal(zeroConst);
+                }
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+            return null;
+        }
+
+        static Cipher aesKeyWrapper = null;
+
+        static {
+            try {
+                aesKeyWrapper = Cipher.getInstance("AESWrap");
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        }
+
+        /**
+         * AES Key Wrapper.
+         * @param message - Message to be wrapped
+         * @param key - Key-Encryption Key
+         * @return Wrapped key
+         */
+        private static byte[] keyWrap(byte[] key, byte[] message) {
+            SecretKey aesKey = new SecretKeySpec(key, 0, 16, "AES");
+            SecretKey messageKey = new SecretKeySpec(message, 0, 16, "AES");
+            try {
+                synchronized (aesKeyWrapper) {
+                    aesKeyWrapper.init(Cipher.WRAP_MODE, aesKey);
+                    byte[] wrappedKey = aesKeyWrapper.wrap(messageKey);
+
+                    /* Unwrap the key */
+                    aesKeyWrapper.init(Cipher.UNWRAP_MODE, aesKey);
+                    messageKey = (SecretKey) aesKeyWrapper.unwrap(wrappedKey, "AES/CTR/NOPADDING", Cipher.SECRET_KEY);
+
+                    return wrappedKey;
+                }
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+            return null;
+        }
+
+    }
+
+    public AescMac() {
+
+    }
+
+    /**
+     * Wrap a message.
+     * @param message - Message to be wrapped
+     * @param key - Key-Encryption Key
+     * @return Wrapped key
+     */
+    public byte[] wrap(byte[] key, byte[] message) {
+        return AesCrypto.keyWrap(key, message);
+    }
+
+    /**
+     * Generate AES message authentication code.
+     * @param key - Key derivation key
+     * @param m - Message to be authenticated
+     * @param len - Length of message in octets
+     * @return Message Authentication Code
+     */
+    public byte[] generateAescMac(byte[] key, byte[] m, int len) {
+
+        initialize(key);
+        update(m, 0, len);
+        return generateMac();
+    }
+
+    /**
+     * Initialize data members.
+     * @param key Key derivation key
+     */
+    private void initialize(byte[] key) {
+        this.key = key;
+        catInput = new ByteArrayOutputStream();
+    }
+
+    /**
+     *  Update the message byte array with offset and length.
+     * @param msg - Message to be authenticated
+     * @param offset - Offset to be added
+     * @param len - Length of the message
+     */
+    private void update(byte[] msg, int offset, int len) {
+        catInput.write(msg, offset, len);
+    }
+
+    /**
+     * Generate MAC.
+     * @return Message authentication code
+     */
+    private byte[] generateMac() {
+        // Step 1
+        Object[] keys = generateSubKey(key);
+        byte[] k1 = (byte[]) keys[0];
+        byte[] k2 = (byte[]) keys[1];
+        byte[] input = catInput.toByteArray();
+
+        // Step 2
+        int numberOfRounds = (input.length + 15) / 16;
+        boolean lastBlockComplete;
+
+        // Step 3
+        if (numberOfRounds == 0) {
+            numberOfRounds = 1;
+            lastBlockComplete = false;
+        } else {
+            lastBlockComplete = (input.length % 16 == 0);
+        }
+
+        byte[] mLast;
+        int srcPos = 16 * (numberOfRounds - 1);
+
+        // Step 4
+        if (lastBlockComplete) {
+            byte[] partInput = new byte[16];
+
+            System.arraycopy(input, srcPos, partInput, 0, 16);
+            mLast = xor128(partInput, k1);
+        } else {
+            byte[] partInput = new byte[input.length % 16];
+
+            System.arraycopy(input, srcPos, partInput, 0, input.length % 16);
+            byte[] padded = padTo16(partInput);
+            mLast = xor128(padded, k2);
+        }
+
+        // Step 5
+        byte[] x = constZero.clone();
+        byte[] partInput = new byte[16];
+        byte[] y;
+
+        // Step 6
+        for (int i = 0; i < numberOfRounds - 1; i++) {
+            srcPos = 16 * i;
+            System.arraycopy(input, srcPos, partInput, 0, 16);
+
+            y = xor128(partInput, x); // Y := Mi (+) X
+            x = AesCrypto.encryptAES128(y, key);
+        }
+
+        y = xor128(x, mLast);
+        x = AesCrypto.encryptAES128(y, key);
+
+        // Step 7
+        return x;
+    }
+
+    /**
+     * Pad to 16 bytes.
+     * @param input - Value to be padded
+     * @return Padded byte array
+     */
+    private byte[] padTo16(byte[] input) {
+        byte[] padded = new byte[16];
+
+        for (int j = 0; j < 16; j++) {
+            if (j < input.length) {
+                padded[j] = input[j];
+            } else if (j == input.length) {
+                padded[j] = (byte) 0x80;
+            } else {
+                padded[j] = (byte) 0x00;
+            }
+        }
+        return padded;
+    }
+
+    /**
+     * Generate Subkey.
+     * @param key - Input key to generate the subkeys
+     * @return Array of two subkeys
+     */
+    private static Object[] generateSubKey(byte[] key) {
+        // Step 1
+        byte[] l = AesCrypto.encryptAES128(constZero, key);
+
+        // Step 2
+        byte[] k1;
+        if ((l[0] & 0x80) == 0) { // If MSB(L) = 0, then K1 = L << 1
+            k1 = leftShiftBit(l);
+        } else {    //Else K1 = ( L << 1 ) (+) Rb
+            byte[] tmp = leftShiftBit(l);
+            k1 = xor128(tmp, constRb);
+        }
+
+        // Step 3
+        byte[] k2;
+        if ((k1[0] & 0x80) == 0) {
+            k2 = leftShiftBit(k1);
+        } else {
+            byte[] tmp = leftShiftBit(k1);
+            k2 = xor128(tmp, constRb);
+        }
+
+        // Step 4
+        Object[] result = new Object[2];
+        result[0] = k1;
+        result[1] = k2;
+        return result;
+    }
+
+    /**
+     * XOR two values.
+     * @param input1 - java.util.byte[]
+     * @param input2 - java.util.byte[]
+     * @return XORed byte array
+     */
+    private static byte[] xor128(byte[] input1, byte[] input2) {
+        byte[] output = new byte[input1.length];
+        for (int i = 0; i < input1.length; i++) {
+            output[i] = (byte) (((int) input1[i] ^ (int) input2[i]) & 0xFF);
+        }
+        return output;
+    }
+
+    /**
+     * Shift left by one bit.
+     * @param input - Value to be shifted
+     * @return Shifted result
+     */
+    private static byte[] leftShiftBit(byte[] input) {
+        byte[] output = new byte[input.length];
+        byte overflow = 0;
+        for (int i = (input.length - 1); i >= 0; i--) {
+            output[i] = (byte) ((int) input[i] << 1 & 0xFF);
+            output[i] |= overflow;
+            overflow = ((input[i] & 0x80) != 0) ? (byte) 1 : (byte) 0;
+        }
+
+        return output;
+    }
+}
+
diff --git a/src/main/java/org/opencord/aaa/CpStateMachine.java b/src/main/java/org/opencord/aaa/CpStateMachine.java
new file mode 100644
index 0000000..8b1ea10
--- /dev/null
+++ b/src/main/java/org/opencord/aaa/CpStateMachine.java
@@ -0,0 +1,577 @@
+/*
+ * Copyright 2018-present. * Open Networking Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.opencord.aaa;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+
+import org.slf4j.Logger;
+import static org.slf4j.LoggerFactory.getLogger;
+
+public class CpStateMachine {
+    private final Logger log = getLogger(getClass());
+    /* State, Transition details. IEEE802.1X-2010. Figure 12-2 */
+    private boolean portValid, newSak;
+    private boolean lrx, ltx, orx, otx;
+    private boolean chgdServer, controlledPortEnabled, chgdCipher;
+    private boolean usingReceiveSAs;
+    private boolean serverTransmitting, usingTransmitSA;
+    private boolean electedSelf, allReceiving, newInfo;
+    private byte[] lki, oki, distributedKI;
+    private byte distributedAN, lan, oan;
+    private long transmitWhen, transmitDelay, retireWhen, retireDelay;
+    /* MACSec management & control */
+    private boolean protectFrames;
+    private boolean replayProtect;
+    private int replayProtectWindow;
+
+    enum MacSecValidate {
+        Disabled, Checked, Strict
+    }
+
+    private MacSecValidate validateFrames;
+    private final int defaultReplayProtectWindow = 10;
+    private final int defaultTransmitDelay = 6000;
+    private final int defaultSakRetireTime = 5000;
+    /* Ciphersuit management & control */
+    private MacSecCipherSuit cipherSuit;
+    enum ConnectType { PENDING, UNAUTHENTICATED, AUTHENTICATED, SECURE }
+    ConnectType connectType;
+    enum StateIndex {
+        BEGIN, INIT, CHANGE, ALLOWED, AUTHENTICATED, SECURED,
+        RECEIVE, RECEIVING, READY, TRANSMIT, TRANSMITTING, ABANDON, RETIRE
+    }
+
+    protected boolean changedConnect() {
+        return (connectType != ConnectType.SECURE) || chgdServer || chgdCipher;
+    }
+
+    abstract class State {
+        StateIndex index;
+        Logger logger;
+        State(StateIndex index, Logger logger) {
+            this.index = index;
+            this.logger = logger;
+        }
+        /**
+         * State Machine state based action function.
+         */
+        public void enter() {
+            logger.trace("Entering state : {} ", index.name());
+        }
+        /**
+         * State Machine state stepping function.
+         * Assumes that before calling "step" atleast once "enter" of the state should be called.
+         * @return null
+         */
+        public StateIndex step() {
+            logger.trace("Stepping from state : {}", index.name());
+            return null;
+        }
+    }
+    class Begin extends State {
+        Begin(Logger logger) {
+            super(StateIndex.BEGIN, logger);
+        }
+        @Override
+        public void enter() {
+            super.enter();
+        }
+        @Override
+        public StateIndex step() {
+            super.step();
+            return StateIndex.INIT;
+        }
+    }
+    class Init extends State {
+        Init(Logger logger) {
+            super(StateIndex.INIT, logger);
+        }
+        @Override
+        public void enter() {
+            controlledPortEnabled = false;
+            /* TODO : */
+            // secY.enablePort(portName, controlledPortEnabled);
+            portValid = false;
+            lki = null;
+            oki = null;
+            lrx = false;
+            ltx = false;
+            orx = false;
+            otx = false;
+        }
+        @Override
+        public StateIndex step() {
+            return StateIndex.CHANGE;
+        }
+    }
+
+    class Change extends State {
+        Change(Logger logger) {
+            super(StateIndex.CHANGE, logger);
+        }
+        @Override
+        public void enter() {
+            super.enter();
+            portValid = false;
+            controlledPortEnabled = false;
+            secY.enablePort(portName, controlledPortEnabled);
+            /* TODO : Delete SAs associated with keys. */
+            if (lki != null) {
+                participant.deleteSAs(lki);
+            }
+            if (oki != null) {
+                participant.deleteSAs(oki);
+            }
+        }
+        @Override
+        public StateIndex step() {
+            super.step();
+            StateIndex index = StateIndex.CHANGE;
+            switch (connectType) {
+                case UNAUTHENTICATED:
+                    index = StateIndex.ALLOWED;
+                    break;
+                case AUTHENTICATED:
+                    index = StateIndex.AUTHENTICATED;
+                    break;
+                case SECURE:
+                    index = StateIndex.SECURED;
+                default:
+                     break;
+            }
+            return index;
+        }
+    }
+    class Allowed extends State {
+        Allowed(Logger logger) {
+            super(StateIndex.ALLOWED, logger);
+        }
+        @Override
+        public void enter() {
+            super.enter();
+            protectFrames = false;
+            replayProtect = false;
+            validateFrames = MacSecValidate.Checked;
+            portValid = false;
+            controlledPortEnabled = true;
+            /* Update SecY */
+            secY.enablePort(portName, controlledPortEnabled);
+            secY.protectFrames(portName, protectFrames);
+            secY.validateFrames(portName, validateFrames);
+            secY.replayProtect(portName, replayProtect, replayProtectWindow);
+        }
+        public StateIndex step() {
+            super.step();
+            StateIndex nextState = StateIndex.ALLOWED;
+            if (connectType != ConnectType.UNAUTHENTICATED) {
+                nextState = StateIndex.CHANGE;
+            }
+            return nextState;
+        }
+    }
+    class Authenticated extends State {
+        Authenticated(Logger logger) {
+            super(StateIndex.AUTHENTICATED, logger);
+        }
+        @Override
+        public void enter() {
+            super.enter();
+            protectFrames = false;
+            replayProtect = false;
+            validateFrames = MacSecValidate.Checked;
+            portValid = false;
+            controlledPortEnabled = true;
+            /* Update SecY */
+            secY.enablePort(portName, controlledPortEnabled);
+            secY.protectFrames(portName, protectFrames);
+            secY.validateFrames(portName, validateFrames);
+            secY.replayProtect(portName, replayProtect, replayProtectWindow);
+        }
+        @Override
+        public StateIndex step() {
+            super.step();
+            StateIndex nextState = StateIndex.AUTHENTICATED;
+            if (connectType != ConnectType.AUTHENTICATED) {
+                nextState = StateIndex.CHANGE;
+            }
+            return nextState;
+        }
+    }
+    class Secured extends State {
+        Secured(Logger logger) {
+            super(StateIndex.SECURED, logger);
+        }
+        @Override
+        public void enter() {
+            super.enter();
+            chgdServer = false;
+            /* TODO : Load from per port configuration. */
+            protectFrames = true;
+            validateFrames = MacSecValidate.Strict;
+            replayProtect = true;
+            replayProtectWindow = defaultReplayProtectWindow;
+            portValid = true;
+            /* Update SecY */
+            secY.setCurrentCipherSuit(portName, cipherSuit.id());
+            /* TODO: Update confidentiality offset from ciphersuit. */
+            secY.protectFrames(portName, protectFrames);
+            secY.validateFrames(portName, validateFrames);
+            secY.replayProtect(portName, replayProtect, replayProtectWindow);
+        }
+        @Override
+        public StateIndex step() {
+            super.step();
+            StateIndex nextState = StateIndex.SECURED;
+            if (changedConnect()) {
+                nextState = StateIndex.CHANGE;
+            } else if (newSak) {
+                nextState = StateIndex.RECEIVE;
+            }
+            return nextState;
+        }
+    }
+    /**
+     * Receive state slighlty differ from IEEE 802.1X 2010 Figure 12-2.
+     * Refer : wpa_supplicant-2.6/src/pae/ieee802_1x_cp.c
+     */
+    class Receive extends State {
+        Receive(Logger logger) {
+            super(StateIndex.RECEIVE, logger);
+        }
+        @Override
+        public void enter() {
+            super.enter();
+            oki = lki;
+            orx = lrx;
+            otx = ltx;
+            oan = lan;
+            lki = distributedKI;
+            lan = distributedAN;
+            newSak = false;
+            /* Update SecY */
+            participant.setOldSAAttributes(oki, oan, orx, otx);
+            participant.setLatestSAAttributes(lki, lan, ltx, lrx);
+            participant.createSAs(lki);
+            participant.enableReceiveSAs(lki);
+        }
+        @Override
+        public StateIndex step() {
+            super.step();
+            StateIndex nextState = StateIndex.RECEIVE;
+            if (usingReceiveSAs) {
+                nextState = StateIndex.RECEIVING;
+            }
+            return nextState;
+        }
+    }
+    class Receiving extends State {
+        Receiving(Logger logger) {
+            super(StateIndex.RECEIVING, logger);
+        }
+        @Override
+        public void enter() {
+            super.enter();
+            lrx = true;
+            participant.setLatestSAAttributes(lki, lan, ltx, lrx);
+            transmitWhen = transmitDelay;
+            /* TODO : CP Timout reset. */
+            usingReceiveSAs = false;
+            serverTransmitting = false;
+        }
+        @Override
+        public StateIndex step() {
+            super.step();
+            StateIndex nextState = StateIndex.RECEIVING;
+            if (newSak || changedConnect()) {
+                nextState = StateIndex.ABANDON;
+            }
+            if (!electedSelf) {
+                nextState = StateIndex.READY;
+            }
+            /* IEEE 802.1X-2010 Figure 12-2 deviation. !controlPortEnabled removed from check. */
+            if (electedSelf && (allReceiving || (transmitWhen == 0))) {
+                nextState = StateIndex.TRANSMIT;
+            }
+            return nextState;
+        }
+    }
+    class Ready extends State {
+        Ready(Logger logger) {
+            super(StateIndex.READY, logger);
+        }
+        @Override
+        public void enter() {
+            super.enter();
+            newInfo = true;
+            // TODO : participant.enableNewInfo();
+        }
+        @Override
+        public StateIndex step() {
+            super.step();
+            StateIndex nextState = StateIndex.READY;
+            if (newSak || changedConnect()) {
+                nextState = StateIndex.ABANDON;
+            }
+            if (serverTransmitting) {
+                nextState = StateIndex.TRANSMIT;
+            }
+            return nextState;
+        }
+    }
+    class Transmit extends State {
+        Transmit(Logger logger) {
+            super(StateIndex.TRANSMIT, logger);
+        }
+        @Override
+        public void enter() {
+            super.enter();
+            controlledPortEnabled = true;
+            secY.enablePort(portName, controlledPortEnabled);
+            ltx = true;
+            participant.setLatestSAAttributes(lki, lan, ltx, lrx);
+            participant.enableTransmitSA(lki);
+            allReceiving = false;
+            serverTransmitting = false;
+        }
+        @Override
+        public StateIndex step() {
+            super.enter();
+            StateIndex nextState = StateIndex.TRANSMIT;
+            if (usingTransmitSA) {
+                nextState = StateIndex.TRANSMITTING;
+            }
+            return nextState;
+        }
+    }
+    class Transmitting extends State {
+        Transmitting(Logger logger) {
+            super(StateIndex.TRANSMITTING, logger);
+        }
+        @Override
+        public void enter() {
+            super.enter();
+            retireWhen = orx ? retireDelay : 0;
+            otx = false;
+            participant.setOldSAAttributes(oki, oan, otx, orx);
+            newInfo = true;
+            // TODO: participant.enableNewInfo();
+            /* TODO : Reset retire when timeout. */
+            usingTransmitSA = false;
+        }
+        @Override
+        public StateIndex step() {
+            super.step();
+            StateIndex nextState = StateIndex.TRANSMITTING;
+            if ((retireWhen == 0) || changedConnect()) {
+                nextState = StateIndex.RETIRE;
+            }
+            return nextState;
+        }
+    }
+    class Abandon extends State {
+        Abandon(Logger logger) {
+            super(StateIndex.ABANDON, logger);
+        }
+        @Override
+        public void enter() {
+            super.enter();
+            lrx = false;
+            participant.setLatestSAAttributes(lki, lan, ltx, lrx);
+            participant.deleteSAs(lki);
+            lki = null;
+            participant.setLatestSAAttributes(lki, lan, ltx, lrx);
+            newSak = false;
+        }
+        public StateIndex step() {
+            super.step();
+            StateIndex nextState = StateIndex.ABANDON;
+            if (changedConnect()) {
+                nextState = StateIndex.RETIRE;
+            } else if (newSak) {
+                nextState = StateIndex.RECEIVE;
+            }
+            return nextState;
+        }
+    }
+    class Retire extends State {
+        Retire(Logger logger) {
+            super(StateIndex.RETIRE, logger);
+        }
+        @Override
+        public void enter() {
+            super.enter();
+            /* IEEE 802.1X-2010 Figure 12-2 deviation. Only clearing old keys. */
+            oki = null;
+            orx = false;
+            otx = false;
+            participant.setOldSAAttributes(oki, oan, otx, orx);
+            /* TODO : deleteSA(oki) ? */
+        }
+        public StateIndex step() {
+            super.step();
+            StateIndex nextState = StateIndex.RETIRE;
+            if (changedConnect()) {
+                nextState = StateIndex.CHANGE;
+            } else if (newSak) {
+                nextState = StateIndex.RECEIVE;
+            }
+            return nextState;
+        }
+    }
+    private final Logger logger = getLogger(getClass());
+    Map<StateIndex, State> states = new HashMap<StateIndex, State>() {{
+        put(StateIndex.BEGIN, new Begin(logger));
+        put(StateIndex.INIT, new Init(logger));
+        put(StateIndex.CHANGE, new Change(logger));
+        put(StateIndex.ALLOWED, new Allowed(logger));
+        put(StateIndex.AUTHENTICATED, new Authenticated(logger));
+        put(StateIndex.SECURED, new Secured(logger));
+        put(StateIndex.RECEIVE, new Receive(logger));
+        put(StateIndex.RECEIVING, new Receiving(logger));
+        put(StateIndex.READY, new Ready(logger));
+        put(StateIndex.TRANSMIT, new Transmit(logger));
+        put(StateIndex.TRANSMITTING, new Transmitting(logger));
+        put(StateIndex.ABANDON, new Abandon(logger));
+        put(StateIndex.RETIRE, new Retire(logger));
+    }};
+    StateIndex currentStateIndex;
+    /* Real worker for State Machine. */
+    private class Worker implements Runnable {
+        public int maxSmIterCount = 100;
+        @Override
+        public void run() {
+            while (true) {
+                try {
+                /* Maximum maxSmIterCount state changes per turn. */
+                    int count = maxSmIterCount;
+                    StateIndex previousState = null;
+                    State currentState = null;
+                    while (count-- > 0) {
+                        previousState = currentStateIndex;
+                        currentState = states.getOrDefault(currentStateIndex, null);
+                        if (currentState != null && (currentState.step() != previousState)) {
+                            currentState.enter();
+                            currentStateIndex = currentState.step();
+                        } else {
+                            break;
+                        }
+                    }
+                    Thread.sleep(1000);
+                } catch (InterruptedException e) {
+                    log.trace("Interruption in CP SM scheduling. {}", e.getMessage());
+                }
+            }
+        }
+    }
+    Worker worker = new Worker();
+    /* State Machine thread pool related components. */
+    ScheduledExecutorService executor;
+    ScheduledFuture<?> future;
+    /* KaY, SecY connections. */
+    MkaParticipant participant;
+    MacSecSecY secY;
+    String portName;
+    /**
+     * Needs participant created properly.
+     *
+     * @param participant   details.
+     * @param secY          SecY interface details.
+     * @param cipherSuit    details.
+     * @param distributedKI KeyIdentifier
+     * @param distributedAN association number
+     * @param executor      scheduled service
+     */
+    public CpStateMachine(MkaParticipant participant, MacSecSecY secY,
+                          MacSecCipherSuit cipherSuit,
+                          byte[] distributedKI,
+                          byte distributedAN,
+                          ScheduledExecutorService executor) {
+        this.secY = secY;
+        this.participant = participant;
+        portName = participant.port().number().name();
+        this.executor = executor;
+        /* Generation default settings. */
+        protectFrames = true;
+        connectType = ConnectType.SECURE;
+        newSak = true;
+        usingReceiveSAs = true;
+        usingTransmitSA = true;
+        /* Validation default settings. */
+        validateFrames = MacSecValidate.Strict;
+        replayProtect = false;
+        replayProtectWindow = defaultReplayProtectWindow;
+        /* Other settings */
+        lrx = true;
+        ltx = true;
+        lki = null;
+        orx = false;
+        otx = false;
+        oki = null;
+        controlledPortEnabled = false;
+        chgdServer = false;
+        this.distributedAN = distributedAN;
+        this.distributedKI = distributedKI;
+        electedSelf = true;
+        /* Ciphersuit */
+        this.cipherSuit = cipherSuit;
+        /* Timers */
+        // TODO : Needs timeout on transmitDelay & retireDelay
+        // transmitDelay = defaultTransmitDelay;
+        transmitDelay = 0;
+        retireDelay = defaultSakRetireTime;
+        currentStateIndex = StateIndex.BEGIN;
+        /* Really apply SecY settings. */
+        secY.setCurrentCipherSuit(portName, cipherSuit.id());
+        secY.protectFrames(portName, protectFrames);
+        secY.validateFrames(portName, validateFrames);
+        secY.replayProtect(portName, replayProtect, replayProtectWindow);
+        secY.enablePort(portName, controlledPortEnabled);
+    }
+    public void startStateMachine() {
+        future = executor.scheduleWithFixedDelay(worker, 1, 1, TimeUnit.MICROSECONDS);
+    }
+    public void stepStateMachine() {
+        future.cancel(false);
+        future = executor.scheduleWithFixedDelay(worker, 1, 1, TimeUnit.MICROSECONDS);
+    }
+    public void setDistributedKI(byte[] ki) {
+        distributedKI = ki;
+    }
+    public void setDistributedAN(byte an) {
+        distributedAN = an;
+    }
+    public void setUsingReceiveSAs(boolean status) {
+        usingReceiveSAs = status;
+    }
+    public void setUsingTransmitSA(boolean status) {
+        usingTransmitSA = status;
+    }
+    public MacSecCipherSuit cipherSuit() {
+        return cipherSuit;
+    }
+    public boolean macSecReplayProtect() {
+        return replayProtect;
+    }
+    public boolean macSecProtect() {
+        return protectFrames;
+    }
+    public MacSecValidate macSecValidate() {
+        return validateFrames; } }
diff --git a/src/main/java/org/opencord/aaa/CustomizationInfo.java b/src/main/java/org/opencord/aaa/CustomizationInfo.java
index d0709f2..1183e67 100755
--- a/src/main/java/org/opencord/aaa/CustomizationInfo.java
+++ b/src/main/java/org/opencord/aaa/CustomizationInfo.java
@@ -19,8 +19,6 @@
 import org.onosproject.net.device.DeviceService;
 import org.opencord.sadis.SubscriberAndDeviceInformationService;
 
-//import java.util.Map;
-
 /**
  * Info required to do customization to packets.
  */
diff --git a/src/main/java/org/opencord/aaa/MacSecAlgorithmAgility.java b/src/main/java/org/opencord/aaa/MacSecAlgorithmAgility.java
new file mode 100644
index 0000000..df04f42
--- /dev/null
+++ b/src/main/java/org/opencord/aaa/MacSecAlgorithmAgility.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2017-present. * Open Networking Foundation .
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.opencord.aaa;
+
+/**
+ * Algorithm Agility.
+ */
+public interface MacSecAlgorithmAgility {
+
+    /* Misc information */
+    public byte[] getParameter();
+
+    /* Various Key Lengths */
+    public int getCakLength();
+    public int getKekLength();
+    public int getIckLength();
+    public int getIcvLength();
+    public int getSakLength();
+
+    /* Transformation Functions */
+    public byte[] generateCak(byte[] msk, byte[] mac1, byte[] mac2);
+    public byte[] generateCkn(byte[] msk, byte[] mac1, byte[] mac2, byte[] sid);
+    public byte[] generateKek(byte[] cak, byte[] ckn);
+    public byte[] generateIck(byte[] cak, byte[] ckn);
+    public byte[] generateIcv(byte[] ick, byte[] msg);
+
+    public byte[] generateSak(byte[] cak, byte[] ksNonce, byte[] memberIDs, int keyNumber);
+
+}
diff --git a/src/main/java/org/opencord/aaa/MacSecCapabilities.java b/src/main/java/org/opencord/aaa/MacSecCapabilities.java
new file mode 100644
index 0000000..8ecbb09
--- /dev/null
+++ b/src/main/java/org/opencord/aaa/MacSecCapabilities.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2018-present. * Open Networking Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.opencord.aaa;
+
+/*
+ * MACSec Capabalities. IEEE 802.1x; Table 11-6
+ */
+public enum MacSecCapabilities {
+    /* Not implemented */
+    NOT_IMPLEMENTED,
+
+    /* 'Integrity without confidentiality' */
+    INTEGRITY,
+
+    /* 'Integrity without confidentiality' and
+       'Integrity and confidentiality' with a confidentiality offset of 0*/
+    INTEGRITY_AND_CONFIDENTIALITY,
+
+    /* 'Integrity without confidentiality' and
+       'Integrity and confidentiality' with a confidentiality offset of 0, 30, 50 */
+    INTEGRITY_AND_CONFIDENTIALITY_0_30_50
+}
diff --git a/src/main/java/org/opencord/aaa/MacSecCipherSuit.java b/src/main/java/org/opencord/aaa/MacSecCipherSuit.java
new file mode 100644
index 0000000..747a36e
--- /dev/null
+++ b/src/main/java/org/opencord/aaa/MacSecCipherSuit.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2017-present. * Open Networking Foundation .
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.opencord.aaa;
+
+/*
+ * Cipher Suit representation. IEEE 802.1AE 2006; Clause 14
+ */
+class MacSecCipherSuit {
+    private Long id;
+    private String name;
+    private MacSecCapabilities capabilities;
+    private MacSecConfidentialityOffset confidentialityOffset;
+    private int keyLength;
+
+    /*
+    * MACSec Capabalities. IEEE 802.1x; Table 11-6
+    */
+    public enum MacSecCapabilities {
+        /* Not implemented */
+        NOT_IMPLEMENTED,
+
+        /* 'Integrity without confidentiality' */
+        INTEGRITY,
+
+        /* 'Integrity without confidentiality' and
+           'Integrity and confidentiality' with a confidentiality offset of 0*/
+        INTEGRITY_AND_CONFIDENTIALITY,
+
+        /* 'Integrity without confidentiality' and
+           'Integrity and confidentiality' with a confidentiality offset of 0, 30, 50 */
+        INTEGRITY_AND_CONFIDENTIALITY_0_30_50
+    }
+
+    /* Confidentiality Offset. */
+    public enum MacSecConfidentialityOffset {
+        /* No confidentiality. */
+        CONFIDENTIALITY_NONE,
+        CONFIDENTIALITY_OFFSET_0,
+        CONFIDENTIALITY_OFFSET_30,
+        CONFIDENTIALITY_OFFSET_50,
+    }
+
+    MacSecCipherSuit() {
+
+        /* Default Cipher Suit. GCM-AES-128. Clause 14.4 */
+        this.id = Long.parseLong("800200"); //Long.parseLong("0080020001000001");
+        this.name = "GCM-AES-128";
+        this.capabilities = MacSecCapabilities.INTEGRITY_AND_CONFIDENTIALITY_0_30_50;
+        keyLength = 16; /* Unit: bytes. ie. 128bits = 16 bytes */
+        confidentialityOffset = MacSecConfidentialityOffset.CONFIDENTIALITY_OFFSET_0;
+    }
+
+    MacSecCipherSuit(Long id, String name, MacSecCapabilities capabilities,
+                     MacSecConfidentialityOffset confidentialityOffset, int keyLength) {
+        this.id = id;
+        this.name = name;
+        this.capabilities = capabilities;
+        this.keyLength = keyLength;
+        this.confidentialityOffset = confidentialityOffset;
+    }
+
+    public int getKeyLength() {
+        return keyLength;
+    }
+
+    public MacSecConfidentialityOffset getConfidentialityOffset() {
+        return confidentialityOffset;
+    }
+
+    public long id() {
+        return id.longValue();
+    }
+}
diff --git a/src/main/java/org/opencord/aaa/MacSecConfig.java b/src/main/java/org/opencord/aaa/MacSecConfig.java
new file mode 100644
index 0000000..47f578b
--- /dev/null
+++ b/src/main/java/org/opencord/aaa/MacSecConfig.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright 2018-present. * Open Networking Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.opencord.aaa;
+
+import org.onosproject.core.ApplicationId;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.config.Config;
+import org.onosproject.net.config.basics.BasicElementConfig;
+
+/**
+ * MACSec config for the AAA app.
+ */
+public class MacSecConfig extends Config<ApplicationId> {
+
+    private static final String VERSION = "version";
+    private static final String CAPABILITY = "capability";
+    private static final String CKN = "ckn";
+    private static final String CAK = "cak";
+    private static final String DEFAULT_NETCONF_DEVICE_ID = "netconfDeviceId";
+
+    //MACSec verion
+    protected static final String DEFAULT_VERSION = "1";
+
+    //MACSec capability
+    protected static final String DEFAULT_CAPABILITY = "0";
+
+    //secure Connectivity Association Key Name
+    protected static final String DEFAULT_CKN = "96437a93ccf10d9dfe347846cce52c7d";
+
+    //secure Connectivity Association Key
+    protected static final String DEFAULT_CAK = "135bd758b0ee5c11c55ff6ab19fdb199";
+
+    //Netconf Device Default value
+    protected static final String DEFAULT_NETCONF_DEVICE = "netconf:192.168.1.2:2022";
+
+    /**
+     * Gets the value of a string property, protecting for an empty JSON object.
+     *
+     * @param name name of the property
+     * @param defaultValue default value if none has been specified
+     * @return String value if one os found, default value otherwise
+     */
+    private String getStringProperty(String name, String defaultValue) {
+        if (object == null) {
+            return defaultValue;
+        }
+        return get(name, defaultValue);
+    }
+
+    /**
+     * Version of MACSEC.
+     * @return MACSec version or null if not set.
+     */
+    public String version() {
+        return getStringProperty(VERSION, DEFAULT_VERSION);
+    }
+
+    /**
+     * Sets the MACSec version.
+     *
+     * @param ver New version; null to clear
+     * @return self
+     */
+    public BasicElementConfig version(String ver) {
+        return (BasicElementConfig) setOrClear(VERSION, ver);
+    }
+
+    /**
+     * Capability of MACSEC.
+     *
+     * @return MACSec capability or null if not set
+     */
+    public String capability() {
+        return getStringProperty(CAPABILITY, DEFAULT_CAPABILITY);
+    }
+
+    /**
+     * Sets the MACSec capability.
+     *
+     * @param cap New capability; null to clear
+     * @return self
+     */
+    public BasicElementConfig capability(String cap) {
+        return (BasicElementConfig) setOrClear(CAPABILITY, cap);
+    }
+
+    /**
+     * CKN.
+     *
+     * @return ckn or null if not set
+     */
+    public String ckn() {
+        return getStringProperty(CKN, DEFAULT_CKN);
+    }
+
+    /**
+     * Sets the CKN.
+     *
+     * @param keyName New CKN; null to clear
+     * @return self
+     */
+    public BasicElementConfig ckn(String keyName) {
+        return (BasicElementConfig) setOrClear(CKN, keyName);
+    }
+
+    /**
+     * CAK.
+     *
+     * @return cak or null if not set
+     */
+    public String cak() {
+        return getStringProperty(CAK, DEFAULT_CAK);
+    }
+
+    /**
+     * Sets the CAK.
+     *
+     * @param key New CAK; null to clear
+     * @return self
+     */
+    public BasicElementConfig cak(String key) {
+        return (BasicElementConfig) setOrClear(CAK, key);
+    }
+
+    /*
+    * Returns default Netconf device Id for SecY interface.
+    * @return Device ID
+    * */
+    public DeviceId netConfDeviceId() {
+        return DeviceId.deviceId(getStringProperty(DEFAULT_NETCONF_DEVICE_ID, DEFAULT_NETCONF_DEVICE));
+    }
+}
+
diff --git a/src/main/java/org/opencord/aaa/MacSecDefaultAlgorithmAgility.java b/src/main/java/org/opencord/aaa/MacSecDefaultAlgorithmAgility.java
new file mode 100644
index 0000000..bff4599
--- /dev/null
+++ b/src/main/java/org/opencord/aaa/MacSecDefaultAlgorithmAgility.java
@@ -0,0 +1,210 @@
+/*
+ * Copyright 2017-present .* Open Networking Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.opencord.aaa;
+
+import java.util.Arrays;
+import java.nio.ByteBuffer;
+import java.io.ByteArrayOutputStream;
+import java.math.BigInteger;
+
+/**
+ * Default algorithm agility implementation. IEEE Std. 802.1X-2009
+ */
+public class MacSecDefaultAlgorithmAgility implements MacSecAlgorithmAgility {
+
+    /* IEEE 802.1X, Table 9-1 */
+    private byte[] parameter = {(byte) 0x00, (byte) 0x80, (byte) 0xC2, (byte) 0x01};
+
+    /* 128 bits for CAK, KEK, ICK, ICV */
+    private int defaultKeyLength = 16;
+
+    /* KDF Parameters */
+    private int kdfContextLength = 16;
+
+    /* Default agility initialization. */
+    MacSecDefaultAlgorithmAgility() {
+    }
+
+    @Override
+    public byte[] getParameter() {
+        return parameter;
+    }
+
+    @Override
+    public int getCakLength() {
+        return defaultKeyLength * 8;
+    }
+
+    @Override
+    public int getKekLength() {
+        return defaultKeyLength * 8;
+    }
+
+    @Override
+    public int getIckLength() {
+        return defaultKeyLength * 8;
+    }
+
+    @Override
+    public int getIcvLength() {
+        return defaultKeyLength * 8;
+    }
+
+    @Override
+    public int getSakLength() {
+        return defaultKeyLength * 8;
+    }
+
+    @Override
+    public byte[] generateCak(byte[] msk, byte[] mac1, byte[] mac2) {
+        /* IEEE 802.1X 2010, Clause 6.2.2. */
+        byte[] cak = null;
+        byte[] label = "IEEE8021 EAP CAK".getBytes();
+        byte[] context = new byte[mac1.length + mac2.length];
+        ByteBuffer contextBuffer = ByteBuffer.wrap(context);
+        contextBuffer.put(mac1);
+        contextBuffer.put(mac2);
+        cak = deriveKey(msk, label, context, (short) getCakLength());
+        return cak;
+    }
+
+    @Override
+    public byte[] generateCkn(byte[] msk, byte[] mac1, byte[] mac2, byte[] sid) {
+        /* IEEE 802.1X 2010, Clause 6.2.2. */
+        byte[] ckn = null;
+        byte[] label = "IEEE8021 EAP CKN".getBytes();
+        byte[] context = new byte[sid.length + mac1.length + mac2.length];
+        ByteBuffer contextBuffer = ByteBuffer.wrap(context);
+        contextBuffer.put(sid);
+        contextBuffer.put(mac1);
+        contextBuffer.put(mac2);
+        ckn = deriveKey(msk, label, context, (short) (defaultKeyLength * 8));
+        return ckn;
+    }
+
+    @Override
+    public byte[] generateKek(byte[] cak, byte[] ckn) {
+        /* IEEE 802.1X 2010, Clause 9.3.3. */
+        byte[] kek = null;
+        byte[] label = "IEEE8021 KEK".getBytes();
+        byte[] keyID = Arrays.copyOfRange(ckn, 0, kdfContextLength);
+        kek = deriveKey(cak, label, keyID, (short) getKekLength());
+        return kek;
+    }
+
+    @Override
+    public byte[] generateIck(byte[] cak, byte[] ckn) {
+        /* IEEE 802.1X 2010, Clause 9.3.3. */
+        byte[] ick = null;
+        byte[] label = "IEEE8021 ICK".getBytes();
+        byte[] keyID = Arrays.copyOfRange(ckn, 0, kdfContextLength);
+        ick = deriveKey(cak, label, keyID, (short) getIckLength());
+        return ick;
+    }
+
+    @Override
+    public byte[] generateIcv(byte[] ick, byte[] msg) {
+        /* IEEE 802.1X-2010 9.4.1
+         * ICV = AES-CMAC(ICK, M, 128) */
+        byte[] icv = null;
+        // icv = prf(ick, msg, 128);
+        icv = prf(ick, msg, msg.length);
+        return icv;
+
+        //return MkaKeyUtils.hexStringToByteArray("045205925831ae59c14550ed59cc003d");
+    }
+
+    @Override
+    public byte[] generateSak(byte[] cak, byte[] ksNonce, byte[] memberIDs, int keyNumber) {
+        byte[] sak = null;
+        byte[] label = "IEEE8021 SAK".getBytes();
+        byte[] kn = ByteBuffer.allocate(4).putInt(keyNumber).array();
+        byte[] context = new byte[ksNonce.length + memberIDs.length + kn.length];
+        ByteBuffer contextBuffer = ByteBuffer.wrap(context);
+        contextBuffer.put(ksNonce);
+        contextBuffer.put(memberIDs);
+        contextBuffer.put(kn);
+        sak = deriveKey(cak, label, context, (short) getSakLength());
+        return sak;
+    }
+
+    /**
+     * Key Derivation Function(KDF). IEEE 802.1X-2010, 6.2.1
+     *
+     * @param key       - Key derivation key either 128 or 256 bits
+     * @param label     - String identifying the purpose of keys derived using KDF
+     * @param context   - Bit string that provides context to identify derived key
+     * @param keyLength - Length of output key in bits encoded in two octets with the most significant octet first
+     * @return Derived Key
+     */
+    private byte[] deriveKey(byte[] key, byte[] label, byte[] context, short keyLength) {
+
+        byte[] length = ByteBuffer.allocate(2).putShort(keyLength).array();
+        int iterations, h;
+        double r = 32;
+
+        // Convert key length into bytes
+        h = key.length;
+
+        // Number of iterations to perform
+        iterations = (key.length * 8 + (h - 1)) / h;
+
+        // Check if iterations exceeds limit of integer capacity
+        if (iterations > (Math.pow(2, r) - 1)) {
+            return null;
+        }
+
+        // Perform key generation
+        ByteArrayOutputStream message = new ByteArrayOutputStream();
+        ByteArrayOutputStream catStream = new ByteArrayOutputStream();
+        try {
+            for (short i = 1; i < iterations; i++) {
+                message.write(i);
+                message.write(label);
+                message.write(0x00);
+                message.write(context);
+                message.write(length);
+                byte[] cat = message.toByteArray();
+                catStream.write(prf(key, cat, cat.length));
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+
+
+        byte[] result = catStream.toByteArray();
+
+        /* Perform shift operation to get the first Length-bits of the result */
+        int bitsToShift = (result.length * 8) - key.length * 8;
+        BigInteger shiftInt = new BigInteger(result).shiftRight(bitsToShift);
+        return shiftInt.toByteArray();
+    }
+
+
+    /**
+     * Pseudo Random Function - Currently AES_CMAC.
+     *
+     * @param key     - Input key.
+     * @param message - Message to be hashed/authenticated.
+     * @param length  - Length of message.
+     * @return Message Authentication Code (MAC).
+     */
+    private static byte[] prf(byte[] key, byte[] message, int length) {
+        return (new AescMac()).generateAescMac(key, message, length);
+    }
+
+}
diff --git a/src/main/java/org/opencord/aaa/MacSecKaY.java b/src/main/java/org/opencord/aaa/MacSecKaY.java
new file mode 100644
index 0000000..b519233
--- /dev/null
+++ b/src/main/java/org/opencord/aaa/MacSecKaY.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2017-present.* Open Networking Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.opencord.aaa;
+
+
+import java.util.AbstractMap;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * MKA KaY( MAC Key Agreement Protocol Entity) per device information.
+ * IEEE 802.1X-2010 Clause 9.16, Figure 12-3.
+ */
+public class MacSecKaY {
+    /* KaY activation  */
+    boolean enable;
+
+    /* MKA Version */
+    private byte mkaVersion = 0x01;
+
+    /* SecY interface */
+    MacSecSecY secY = null;
+
+    /* Algorithm Agility. IEEE 802.1X 2010; Table 9.1.
+     * Currently standard supports only one. List is used considering future additions. */
+    private List<MacSecAlgorithmAgility> agilities = new ArrayList<>();
+    private static int defaultAlgorithmAgility = 0;
+
+    /* CipherSuits supported by device. */
+    private List<MacSecCipherSuit> cipherSuits = new ArrayList<>();
+    private static int defaultCipherSuitOffset = 0;
+
+    /* Participants & CP state machine store.  */
+    private Map<String, Map.Entry<MkaParticipant, CpStateMachine>> portStoreMap =
+            new ConcurrentHashMap<>();
+
+    /* Load all SecY supporting CipherSuits */
+    private void loadCipherSuits() {
+        /* TODO : Load CipherSuits using SecY. */
+    }
+
+    MacSecKaY(MacSecSecY secY) {
+        /* Initialize default algorithm agility. IEEE 802.1X; Table 9.1*/
+        agilities.add(defaultAlgorithmAgility, new MacSecDefaultAlgorithmAgility());
+
+        /* Initialize CipherSuits.
+         * All devices should support default CipherSuit GCM–AES–128. IEEE 802.1AE 14.5
+         */
+        this.secY = secY;
+        cipherSuits.add(defaultCipherSuitOffset, new MacSecCipherSuit());
+        loadCipherSuits();
+    }
+
+    /**
+     * Adding participants & CP state machine to KaY.
+     *
+     * @param id             value in <Device ID><Port ID> format
+     * @param participant    details
+     * @param cpStateMachine details
+     */
+    public void addPortDetails(String id, MkaParticipant participant, CpStateMachine cpStateMachine) {
+        portStoreMap.put(id, new AbstractMap.SimpleEntry<>(participant, cpStateMachine));
+    }
+
+    /* Retrieve Participant using CAK */
+    public MkaParticipant participant(byte[] ckn) {
+        Optional<Map.Entry<String, Map.Entry<MkaParticipant, CpStateMachine>>> participant =
+                portStoreMap.entrySet().stream()
+                        .filter(entry -> entry.getValue().getKey().cak().equals(ckn))
+                        .findFirst();
+        return participant.isPresent() ? participant.get().getValue().getKey() : null;
+    }
+
+    /* Participant by ID. */
+    public MkaParticipant lookupParticipantByID(String id) {
+        Map.Entry<MkaParticipant, CpStateMachine> portDetails = portStoreMap.getOrDefault(id, null);
+        return (portDetails != null) ? portDetails.getKey() : null;
+    }
+
+    /* CP state machine by ID */
+    public CpStateMachine lookupCPStateMachineByID(String id) {
+        Map.Entry<MkaParticipant, CpStateMachine> portDetails = portStoreMap.getOrDefault(id, null);
+        return (portDetails != null) ? portDetails.getValue() : null;
+    }
+
+    /* Default algorithm agility. */
+    public MacSecAlgorithmAgility defaultAlgorithmAgility() {
+        return agilities.get(defaultAlgorithmAgility);
+    }
+
+    /* Default cipher suit. */
+    public MacSecCipherSuit defaultCipherSuit() {
+        return cipherSuits.get(defaultCipherSuitOffset);
+    }
+
+    /* Version */
+    public byte mkaVersion() {
+        return mkaVersion;
+    }
+ }
diff --git a/src/main/java/org/opencord/aaa/MacSecSecY.java b/src/main/java/org/opencord/aaa/MacSecSecY.java
new file mode 100644
index 0000000..d7792ec
--- /dev/null
+++ b/src/main/java/org/opencord/aaa/MacSecSecY.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright 2017-present. * Open Networking Foundation .
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.opencord.aaa;
+
+import org.slf4j.Logger;
+
+import org.onlab.packet.EAPOL_MKPDU_BasicParameterSet.SCI;
+
+import org.onosproject.net.DeviceId;
+
+import org.opencord.aaa.CpStateMachine.MacSecValidate;
+
+import java.util.List;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+public class MacSecSecY {
+
+    private final Logger log = getLogger(getClass());
+    DeviceId deviceId;
+
+    MacSecSecY(DeviceId deviceId) {
+        this.deviceId = deviceId;
+    }
+
+    /* Verification */
+    public boolean validateFrames(String portName, MacSecValidate validate) {
+        log.trace("Request for Validate Frames: {} {}", portName, validate);
+        return true;
+    }
+
+    /* Generation */
+    public boolean protectFrames(String portName, boolean status) {
+        log.trace("Request for enabling/disabling Protect Frames : {} {}", portName, status);
+        return true;
+    }
+
+    /* Transmit & Receive SCs & SAs. */
+    public boolean createTransmitSC(String portName, SCI sci) {
+        log.trace("Transmit SC creation request : {}, {}", portName, sci);
+        return true;
+    }
+
+    public boolean deleteTransmitSC(String portName, SCI sci) {
+        log.trace("Transmit SC delete request : [{}, {}]", portName, sci);
+        return true;
+    }
+
+    public boolean createTransmitSA(String portName, SCI sci, int an, long pn) {
+        log.trace("Transmit SA creation request : [{}, {}, {}, {}]", portName, sci, an, pn);
+        return true;
+    }
+
+    public boolean deleteTransmitSA(String portName, SCI sci, int an) {
+        log.trace("Transmit SA deletion request : [{},{},{},{}]", portName, sci, an);
+        return true;
+    }
+
+    public boolean enableTransmitSA(String portName, SCI sci, int an) {
+        log.trace("Transmit SA enable request : [{},{},{},{}]", portName, sci, an);
+        return true;
+    }
+
+    public boolean createReceiveSC(String portName, SCI sci) {
+        log.trace("Receive SC creation request : [{}, {}]", portName, sci);
+        return true;
+    }
+
+    public boolean deleteReceiveSC(String portName, SCI sci) {
+        log.trace("Receive SC deletion request : [{}, {}]", portName, sci);
+        return true;
+    }
+
+    public boolean createReceiveSA(String portName, SCI sci, int an, long pn, long lowestPn) {
+        log.trace("Receive SA creation request : [{}, {}, {}, {}, {}]", portName, sci, an, pn,
+                lowestPn);
+        return true;
+    }
+
+    public boolean deleteReceiveSA(String portName, SCI sci, int an) {
+        log.trace("Receive SA deletion request : [{},{},{},{}]", portName, sci, an);
+        return true;
+    }
+
+    public boolean enableReceiveSA(String portName, SCI sci, int an) {
+        log.trace("Receive SA enable request : [{},{},{},{}]", portName, sci, an);
+        return true;
+    }
+
+    /* SAK Keys. */
+    public boolean installKey(String portName, int index, byte[] key, byte[] keyIdentifier,
+                              boolean transmits, boolean receives) {
+        log.trace("Received Key Installation request : {} {} {} {} {} {}", portName, index, key,
+                keyIdentifier, transmits, receives);
+        return true;
+    }
+
+    public boolean removeKey(String portName, int index) {
+        log.trace("Received Key Removal request : {} {}", portName, index);
+        return true;
+    }
+
+    /* CipherSuit configuration. */
+    public boolean addCipherSuite(int cipherSuitID, boolean enable,
+                                     boolean confidentiality) {
+        return true;
+    }
+
+    public boolean setCurrentCipherSuit(String portName, long cipherSuitID) {
+        log.trace("Request for setting current Cipher Suit : {} {}", portName, cipherSuitID);
+        return true;
+    }
+
+    public List<MacSecCipherSuit> getCipherSuites() {
+        return null;
+    }
+
+    /* Port enable/disable*/
+    public boolean enablePort(String portName, boolean status) {
+        log.trace("Enable port request : {} {} {}", portName, status);
+        return true;
+    }
+
+    public boolean replayProtect(String portName, boolean status, int window) {
+        log.trace("Replay protection request : {} {} {}", portName, status, window);
+        return true;
+    }
+}
+
diff --git a/src/main/java/org/opencord/aaa/MkaKeyUtils.java b/src/main/java/org/opencord/aaa/MkaKeyUtils.java
new file mode 100644
index 0000000..3aa7a1e
--- /dev/null
+++ b/src/main/java/org/opencord/aaa/MkaKeyUtils.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2018-present. * Open Networking Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.opencord.aaa;
+
+/**
+ * Misc. MKA key utility.
+ */
+public final class MkaKeyUtils {
+
+    static final char[] HEX_DIGITS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
+
+    private MkaKeyUtils(){
+
+    }
+    /**
+     * Convert hex string to byte array.
+     *
+     * @param s - Hex string to be converted to byte array
+     * @return byte array formed from the string
+     */
+    public static byte[] hexStringToByteArray(String s) {
+        int len;
+        byte[] data;
+        if (s == null) {
+            return null;
+        }
+
+        len = s.length();
+        data = new byte[len / 2]; /* Two characters forms one byte. */
+        for (int i = 0; i < len; i += 2) {
+            data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character.digit(s.charAt(i + 1), 16));
+        }
+        return data;
+    }
+
+    /**
+     * Utility to convert byte to hex string.
+     *
+     * @param data - Byte array to be converted to hex string
+     * @return - String representing ASCII hex string of given data.
+     */
+    public static String byteArrayToHexString(byte[] data) {
+        final char[] hexString = new char[data.length << 1];
+        if (data == null) {
+            return null;
+        }
+
+        for (int i = 0, j = 0; i < data.length; i++) {
+            hexString[j++] = HEX_DIGITS[(data[i] & 0xF0) >>> 4];
+            hexString[j++] = HEX_DIGITS[data[i] & 0x0F];
+        }
+        return new String(hexString);
+    }
+
+}
diff --git a/src/main/java/org/opencord/aaa/MkaParticipant.java b/src/main/java/org/opencord/aaa/MkaParticipant.java
new file mode 100644
index 0000000..63fa6a6
--- /dev/null
+++ b/src/main/java/org/opencord/aaa/MkaParticipant.java
@@ -0,0 +1,1340 @@
+/*
+ * Copyright 2017-present. * Open Networking Foundation .
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.opencord.aaa;
+
+// import com.sun.xml.internal.bind.v2.runtime.reflect.Lister;
+
+
+import org.onlab.packet.EAPOL;
+import org.onlab.packet.EthType;
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.EAPOL_MKPDU;
+import org.onlab.packet.EAPOL_MKPDU_ParameterSet;
+import org.onlab.packet.EAPOL_MKPDU_BasicParameterSet;
+import org.onlab.packet.EAPOL_MKPDU_BasicParameterSet.SCI;
+import org.onlab.packet.EAPOL_MKPDU_PeerListParameterSet;
+import org.onlab.packet.EAPOL_MKPDU_PeerListParameterSet.MemberDetails;
+import org.onlab.packet.EAPOL_MKPDU_MACSecUseParameterSet;
+import org.onlab.packet.EAPOL_MKPDU_DistributedSAKParameterSet;
+import org.onlab.packet.EAPOL_MKPDU_ICVIndicatorParameterSet;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Port;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.packet.DefaultOutboundPacket;
+import org.onosproject.net.packet.OutboundPacket;
+import org.onosproject.net.packet.PacketService;
+
+import java.nio.ByteBuffer;
+import java.security.SecureRandom;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.slf4j.Logger;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * MKA KaY( MAC Key Agreement Protocol Entity) participants coming under one CA.
+ * IEEE 802.1X-2010 Clause 9.16, Figure 12-3
+ */
+public class MkaParticipant {
+
+    /* Logger */
+    private final Logger log = getLogger(getClass());
+
+    /* KaY(parent), SecY interfaces*/
+    MacSecKaY kay;
+    MacSecSecY secY;
+
+    /* SCI */
+    private SCI actorSci;
+
+    /* Key Details */
+    private byte[] cak;
+    private byte[] ckn;
+    private byte[] kek;
+    private byte[] ick;
+    private byte[] sak;
+
+    /* Actor Details. ie. myself as Actor. */
+    private byte actorPriority;
+    private byte[] mi;
+    private int mn;
+
+    /* Port specific management, monitor & control */
+    private boolean active;
+
+    public enum ActivateType {
+        DEFAULT,
+        DISABLED,
+        ON_OPER_UP,
+        ALWAYS
+    }
+
+    private ActivateType activate;
+    private boolean principal;
+
+    /* MACSec specific management, monitor & control. */
+    /* MACSec Capabalities. IEEE 802.1x; Table 11-6 */
+    private MacSecCipherSuit.MacSecCapabilities macSecCapability;
+    private boolean macSecDesired;
+
+    private List<MkaPeer> livePeers = new ArrayList<>();
+    private List<MkaPeer> potentialPeers = new ArrayList<>();
+
+    /* Latest Key Server Details. ie. Details of Key Server for current SAK.  */
+    private byte[] latestKI;
+    private int latestKN;
+    private boolean latestTX;
+    private boolean latestRX;
+    private byte latestAN;
+
+    /* Old Key Server Details. ie. Details of Key Server for previous SAK. */
+    private byte[] oldKI;
+    private int oldKN;
+    private boolean oldTX;
+    private boolean oldRX;
+    private byte oldAN;
+
+    private final byte[] emptyKI = new byte[EAPOL_MKPDU_ParameterSet.FIELD_MI_LENGTH];
+
+    /* AAA State-Machine; for Device, Supplicant access  */
+    private StateMachine sm;
+
+    /* Packet Service for packet transmission. */
+    private PacketService ps;
+    private DeviceId deviceId;
+    private Port port;
+
+    /* Key Store */
+    public class KeyStore {
+        class Key {
+            int keyIndex;
+            ByteBuffer keyIdentifier;
+            ByteBuffer key;
+            long createdTime;
+            boolean transmits, receives;
+
+            Key(int keyIndex, byte[] keyIdentifier, byte[] key) {
+                this.keyIndex = keyIndex;
+                this.keyIdentifier = ByteBuffer.wrap(keyIdentifier);
+                this.key = ByteBuffer.wrap(key);
+                createdTime = System.currentTimeMillis();
+                transmits = true;
+                receives = true;
+            }
+
+            public byte[] key() {
+                return key.array();
+            }
+
+            public ByteBuffer keyBuffer() {
+                return key;
+            }
+
+            public byte[] keyIdentifier() {
+                return keyIdentifier.array();
+            }
+
+            public int keyIndex() {
+                return keyIndex;
+            }
+
+            public ByteBuffer keyIdentifierBuffer() {
+                return keyIdentifier;
+            }
+
+
+         /*   @Override
+            public boolean equals(Object object) {
+
+                if ((object instanceof Key) && ((Key) object).keyBuffer().equals(key))
+                    return true;
+                else if ((object instanceof ByteBuffer) && ((ByteBuffer) object).equals(keyIdentifier))
+                    return true;
+                return false;
+            }*/
+
+            @Override
+            public int hashCode() {
+                return keyIdentifier().hashCode();
+            }
+
+            public void setTransmits(boolean status) {
+                transmits = status;
+            }
+
+            public boolean isTransmits() {
+                return transmits;
+            }
+
+            public void setReceives(boolean status) {
+                receives = status;
+            }
+
+            public boolean isReceives() {
+                return receives;
+            }
+        }
+
+        class Store {
+            Map<Key, List<SecureAssociation>> store = new LinkedHashMap<>();
+
+            public void put(Key key, List<SecureAssociation> value) {
+                store.put(key, value);
+            }
+
+            public List<SecureAssociation> get(ByteBuffer keyID) {
+                Optional<Key> key = store.keySet().stream()
+                        .filter(k -> k.keyIdentifierBuffer().equals(keyID))
+                        .findFirst();
+                List<SecureAssociation> x = key.isPresent() ? store.get(key.get()) : null;
+                return x;
+//                return (key != null) ? store.get(key) : null;
+            }
+
+            public Set<Key> keySet() {
+                return store.keySet();
+            }
+
+        }
+
+        Store store = null;
+        AtomicInteger indexSequence = null;
+
+        KeyStore() {
+            store = new Store();
+            indexSequence = new AtomicInteger();
+        }
+
+        public void storeSA(byte[] keyIdentifier, SecureAssociation sa) {
+            ByteBuffer keyIDBuffer = ByteBuffer.wrap(keyIdentifier);
+            List<SecureAssociation> saList = store.get(keyIDBuffer);
+            if (saList != null) {
+                saList.add(sa);
+            }
+        }
+
+        public List<SecureAssociation> retrieveSAs(byte[] keyIdentifier) {
+            ByteBuffer keyIDBuffer = ByteBuffer.wrap(keyIdentifier);
+            return store.get(keyIDBuffer);
+
+            //return store.get(keyIDBuffer);
+        }
+
+        public void transmits(byte[] keyIdentifier, boolean status) {
+            ByteBuffer keyIDBuffer = ByteBuffer.wrap(keyIdentifier);
+            Optional<Key> keyEntry = store.keySet().stream()
+                    .filter(k -> k.equals(keyIDBuffer))
+                    .findFirst();
+            keyEntry.ifPresent(k -> k.setTransmits(status));
+        }
+
+        public boolean isTransmits(byte[] keyIdentifier) {
+            ByteBuffer keyIDBuffer = ByteBuffer.wrap(keyIdentifier);
+            Key keyEntry = store.keySet().stream()
+                    .filter(k -> k.equals(keyIDBuffer))
+                    .findFirst()
+                    .get();
+            return (keyEntry != null) ? keyEntry.isTransmits() : false;
+        }
+
+        public void receives(byte[] keyIdentifier, boolean status) {
+            ByteBuffer keyIDBuffer = ByteBuffer.wrap(keyIdentifier);
+            Optional<Key> keyEntry = store.keySet().stream()
+                    .filter(k -> k.equals(keyIDBuffer))
+                    .findFirst();
+            keyEntry.ifPresent(k -> k.setReceives(status));
+        }
+
+        public boolean isReceives(byte[] keyIdentifier) {
+            ByteBuffer keyIDBuffer = ByteBuffer.wrap(keyIdentifier);
+            Key keyEntry = store.keySet().stream()
+                    .filter(k -> k.equals(keyIDBuffer))
+                    .findFirst()
+                    .get();
+            return (keyEntry != null) ? keyEntry.isReceives() : false;
+        }
+
+        public void createKey(byte[] keyIdentifier, byte[] key) {
+            ByteBuffer keyIDBuffer = ByteBuffer.wrap(keyIdentifier);
+            List<SecureAssociation> saList = store.get(keyIDBuffer);
+            if (saList == null) {
+                saList = new LinkedList<>();
+                Key index = new Key(indexSequence.getAndIncrement() % Byte.MAX_VALUE,
+                        keyIdentifier, key);
+                store.put(index, saList);
+                secY.installKey(port.number().name(), index.keyIndex(), index.key(),
+                        index.keyIdentifier(), index.isTransmits(), index.isReceives());
+            }
+        }
+    }
+
+    KeyStore keyStore = null;
+
+    /* General Secure Connection & Secure Associations. IEEE 802.1AE SecY YANG model. */
+    enum SCType {
+        GENERATION, VERIFICATION
+    }
+
+    abstract class SecureConnection {
+        SCI sci = null;
+        long createdTime;
+        SCType type;
+        AtomicInteger anIndex = new AtomicInteger();
+        List<SecureAssociation> sas = new LinkedList<>();
+
+        SecureConnection(SCI sci, SCType type) {
+            this.sci = sci;
+            this.type = type;
+            createdTime = System.currentTimeMillis();
+        }
+
+        public void attachSA(SecureAssociation sa) {
+            sas.add(sa);
+        }
+
+        public void detachSA(SecureAssociation sa) {
+            sas.removeIf(s -> s.equals(sa));
+        }
+
+        public byte getNextAvailableAN() {
+            return (byte) (anIndex.getAndIncrement() % Byte.MAX_VALUE);
+        }
+
+        public SCI sci() {
+            return sci;
+        }
+
+        public SCType type() {
+            return type;
+        }
+    }
+
+    abstract class SecureAssociation {
+        SecureConnection parent;
+        byte an;
+        long createdTime;
+        boolean inUse;
+
+        SecureAssociation(SecureConnection sc, byte an) {
+            parent = sc;
+            this.an = an;
+            createdTime = System.currentTimeMillis();
+            inUse = false;
+        }
+
+        public SecureConnection parent() {
+            return parent;
+        }
+
+        public byte an() {
+            return an;
+        }
+
+        public void setInUse(boolean status) {
+            inUse = status;
+        }
+    }
+
+    /* Transmit SC. */
+    class TransmitSC extends SecureConnection {
+        byte encodingSA;
+        boolean transmitting;
+
+        /* Transmit SAs */
+        class TransmitSA extends SecureAssociation {
+            long nextPN;
+            boolean confidentiality;
+
+            TransmitSA(TransmitSC sc, byte an, long nextPN) {
+                super(sc, an);
+                this.nextPN = nextPN;
+
+                /* TODO : Confidentiality determine from offset. */
+                /* confidentiality = ? */
+            }
+
+            public long nextPN() {
+                return nextPN;
+            }
+        }
+
+        TransmitSC(SCI sci) {
+            super(sci, SCType.GENERATION);
+            encodingSA = (byte) 0x00;
+            transmitting = false;
+        }
+
+        public SecureAssociation createSA(byte an, long nextPN) {
+            TransmitSA sa = new TransmitSA(this, an, nextPN);
+            attachSA(sa);
+            return sa;
+        }
+    }
+
+    TransmitSC txSC = null;
+
+    /* Receive SC. IEEE 802.1AE SecY YANG model. */
+    class ReceiveSC extends SecureConnection {
+
+        boolean receiving;
+
+        ReceiveSC(SCI sci) {
+            super(sci, SCType.VERIFICATION);
+            receiving = false;
+        }
+
+        class ReceiveSA extends SecureAssociation {
+            long nextPN, lowestPN;
+
+            ReceiveSA(ReceiveSC sc, byte an, long pn, long lpn) {
+                super(sc, an);
+                nextPN = pn;
+                lowestPN = lpn;
+            }
+
+            public long nextPN() {
+                return nextPN;
+            }
+
+            public long lowestPN() {
+                return lowestPN;
+            }
+
+        }
+
+        public SecureAssociation createSA(byte an, long pn, long lpn) {
+            ReceiveSA sa = new ReceiveSA(this, an, pn, lpn);
+            attachSA(sa);
+            return sa;
+        }
+
+    }
+
+    LinkedList<ReceiveSC> rxSCList = new LinkedList<>();
+
+    /* EAPOL-MKPDU serialization/de-serialization supporters.
+     * ie. ParameterSet Creators & Processors  */
+    abstract class ParameterSetCreator {
+        MacSecKaY kaY;
+        MkaParticipant participant;
+
+        public ParameterSetCreator(MacSecKaY kaY, MkaParticipant participant) {
+            this.kaY = kaY;
+            this.participant = participant;
+        }
+
+        /* Building parameter set. */
+        abstract EAPOL_MKPDU_ParameterSet buildParameterSet();
+
+        /* Identifying type of parameter set supporting. */
+        abstract int parameterSetType();
+    }
+
+    /* Basic Parameter Set Creator */
+    class BasicParameterSetCreator extends ParameterSetCreator {
+        MacSecKaY kaY;
+        MkaParticipant participant;
+
+        BasicParameterSetCreator(MacSecKaY kaY, MkaParticipant participant) {
+            super(kaY, participant);
+            this.kaY = kay;
+            this.participant = participant;
+        }
+
+        @Override
+        EAPOL_MKPDU_ParameterSet buildParameterSet() {
+            EAPOL_MKPDU_BasicParameterSet bps = new EAPOL_MKPDU_BasicParameterSet();
+            bps.setMkaVersion(kay.mkaVersion());
+            bps.setKeyServerPriority(participant.actorPriority());
+                /* TODO: KeyServer Election not done.
+                 * So setting myself as infrastructure key server.
+                 */
+            bps.setKeyServer(true);
+            bps.setMacSecDesired(participant.macSecDesired);
+            bps.setMacSecCapability((byte) participant.macSecCapability.ordinal());
+            bps.setSci(participant.actorSci());
+            bps.setActorMI(participant.actorMI());
+            bps.setActorMN(participant.retrieveAndUpdateMN());
+            bps.setAlgAgility(kay.defaultAlgorithmAgility().getParameter());
+            bps.setCKN(participant.ckn());
+            return (EAPOL_MKPDU_ParameterSet) bps;
+        }
+        @Override
+        int parameterSetType() {
+            return EAPOL_MKPDU_ParameterSet.PARAMETERSET_TYPE_BASIC;
+        }
+    }
+
+    /* Live Peer List Parameter Set Creator */
+    class LivePeerListCreator extends ParameterSetCreator {
+        MacSecKaY kaY;
+        MkaParticipant participant;
+
+        LivePeerListCreator(MacSecKaY kaY, MkaParticipant participant) {
+            super(kaY, participant);
+            this.kaY = kay;
+            this.participant = participant;
+        }
+
+        @Override
+        EAPOL_MKPDU_ParameterSet buildParameterSet() {
+                /* Ensure Live Peers are existing. */
+            List<MkaPeer> peers = participant.allLivePeers();
+            if (peers.isEmpty()) {
+                return null;
+            }
+                /* Populate Peer list. */
+            EAPOL_MKPDU_PeerListParameterSet lplps =
+                    new EAPOL_MKPDU_PeerListParameterSet();
+            lplps.setPeerListType(EAPOL_MKPDU_PeerListParameterSet.PEERLIST_TYPE_LIVE);
+            peers.forEach((peer) -> {
+                lplps.addMember(peer.mi(), peer.mn());
+            });
+            return (EAPOL_MKPDU_ParameterSet) lplps;
+        }
+        @Override
+        int parameterSetType() {
+            return EAPOL_MKPDU_PeerListParameterSet.PEERLIST_TYPE_LIVE;
+        }
+    }
+
+    /* Potential Parameter Set Creator */
+    class PotentialPeerListCreator extends ParameterSetCreator {
+        MacSecKaY kaY;
+        MkaParticipant participant;
+
+        PotentialPeerListCreator(MacSecKaY kaY, MkaParticipant participant) {
+            super(kaY, participant);
+            this.kaY = kay;
+            this.participant = participant;
+        }
+
+        @Override
+        EAPOL_MKPDU_ParameterSet buildParameterSet() {
+                /* Ensure Potential Peers are existing. */
+            List<MkaPeer> peers = participant.allPotentialPeers();
+            if (peers.isEmpty()) {
+                return null;
+            }
+                /* Populate Peer list. */
+            EAPOL_MKPDU_PeerListParameterSet pplps =
+                    new EAPOL_MKPDU_PeerListParameterSet();
+            pplps.setPeerListType(EAPOL_MKPDU_PeerListParameterSet.PEERLIST_TYPE_POTENTIAL);
+            peers.forEach((i) -> {
+                pplps.addMember(i.mi(), i.mn());
+            });
+            return (EAPOL_MKPDU_ParameterSet) pplps;
+        }
+        @Override
+        int parameterSetType() {
+            return EAPOL_MKPDU_PeerListParameterSet.PEERLIST_TYPE_POTENTIAL;
+        }
+    }
+
+    /* MACSec Use Parameter Set Creator */
+    class MacSecUseCreator extends ParameterSetCreator {
+        MacSecKaY kaY;
+        MkaParticipant participant;
+
+        MacSecUseCreator(MacSecKaY kaY, MkaParticipant participant) {
+            super(kaY, participant);
+            this.kaY = kay;
+            this.participant = participant;
+        }
+
+        @Override
+        EAPOL_MKPDU_ParameterSet buildParameterSet() {
+            EAPOL_MKPDU_MACSecUseParameterSet ups =
+                    new EAPOL_MKPDU_MACSecUseParameterSet();
+            CpStateMachine cpSM = kay.lookupCPStateMachineByID(sm.sessionId());
+            ups.setDelayProtect(cpSM.macSecReplayProtect());
+            ups.setPlainTX(!cpSM.macSecProtect());
+            ups.setPlainRX((cpSM.macSecValidate() == CpStateMachine.MacSecValidate.Disabled) ? true : false);
+
+            // Latest Key Server Details
+            ups.setLatestKI(participant.latestKI());
+            ups.setLatestKN(participant.latestKN());
+            ups.setLatestTX(participant.latestTX());
+            ups.setLatestRX(participant.latestRX());
+            ups.setLatestAN(participant.latestAN());
+
+            /* TODO: Filling Latest Lowest Acceptable PN. */
+            // Retrieve PN for Key Server.
+            // int pn = DEVICE-Abstract-Layer.getPN(participant.latestKI())
+            // ups.setLatestLAPN(pn)
+
+            // Old Key Server Details.
+            /* TODO: Needs rethinking for newly joined actors. */
+            ups.setOldKI(participant.oldKI());
+            ups.setOldKN(participant.oldKN());
+            ups.setOldTX(participant.oldTX());
+            ups.setOldRX(participant.oldRX());
+            ups.setOldAN(participant.oldAN());
+
+            /* TODO: Filling Old Lowest Acceptable PN. */
+            // Retrieve PN for Key Server.
+            // int opn = DEVICE-Abstract-Layer.getPN(participant.oldKI())
+            // ups.setLatestLAPN(opn)
+            return (EAPOL_MKPDU_ParameterSet) ups; }
+        @Override
+        int parameterSetType() {
+            return EAPOL_MKPDU_ParameterSet.PARAMETERSET_TYPE_MACSEC_SAK_USE;
+        }
+    }
+
+    /* Distributed SAK Parameter Set Creator */
+    class DistributedSakCreator extends ParameterSetCreator {
+        MacSecKaY kaY;
+        MkaParticipant participant;
+
+        DistributedSakCreator(MacSecKaY kaY, MkaParticipant participant) {
+            super(kaY, participant);
+            this.kaY = kay;
+            this.participant = participant;
+        }
+
+        @Override
+        EAPOL_MKPDU_ParameterSet buildParameterSet() {
+            EAPOL_MKPDU_DistributedSAKParameterSet dsps =
+                    new EAPOL_MKPDU_DistributedSAKParameterSet();
+            dsps.setDistributedAN(participant.latestAN());
+            CpStateMachine cpSM = kay.lookupCPStateMachineByID(sm.sessionId());
+            dsps.setConfidentialityOffset((byte) cpSM.cipherSuit()
+                    .getConfidentialityOffset().ordinal());
+            dsps.setKeyNumber(participant.latestKN());
+            dsps.setSAK(participant.sak());
+            dsps.setKeyWrapper((byte[] key) -> {
+                byte[] wrappedKey = new AescMac().wrap(participant.kek(), key);
+                return wrappedKey;
+            });
+            return (EAPOL_MKPDU_ParameterSet) dsps;
+        }
+        @Override
+        int parameterSetType() {
+            return EAPOL_MKPDU_ParameterSet.PARAMETERSET_TYPE_DISTRIBUTED_SAK;
+        }
+    }
+
+    /* ICV Parameter Set Creator */
+    class IcvCreator extends  ParameterSetCreator {
+
+        MacSecKaY kaY;
+        MkaParticipant participant;
+
+        IcvCreator(MacSecKaY kaY, MkaParticipant participant) {
+            super(kaY, participant);
+            this.kaY = kay;
+            this.participant = participant;
+        }
+
+        @Override
+        EAPOL_MKPDU_ParameterSet buildParameterSet() {
+            EAPOL_MKPDU_ICVIndicatorParameterSet icvps = new EAPOL_MKPDU_ICVIndicatorParameterSet();
+                /* Populate PS fields. */
+//                icvps.setKey(participant.ick());
+//                 class AESCMAC_128_HashCreator implements EAPOL_MKPDU_ICVIndicatorParameterSet.HashGenerator<byte[]> {
+//                     @Override
+//                     public byte[] generateHash(byte[] key, byte[] msg) {
+//                         return participant.kay.defaultAlgorithmAgility().generateICV(key, msg);
+//                     }
+//                 }
+//                 icvps.setHashCreator(new AESCMAC_128_HashCreator());
+//                icvps.setHashCreator((byte[] key, byte[] msg)
+//                                      -> (kay.defaultAlgorithmAgility().generateICV(key, msg)));
+            return (EAPOL_MKPDU_ParameterSet) icvps;
+        }
+        @Override
+        int parameterSetType() {
+            return EAPOL_MKPDU_ParameterSet.PARAMETERSET_TYPE_ICV_INDICATOR;
+        }
+    }
+
+    Map<Byte, ParameterSetCreator> psCreators = new LinkedHashMap<>();
+
+    /* Parameter Set Processors */
+    abstract class ParameterSetProcessor {
+        MacSecKaY kaY;
+        MkaParticipant participant;
+
+        public ParameterSetProcessor(MacSecKaY kaY, MkaParticipant participant) {
+            this.kaY = kaY;
+            this.participant = participant;
+        }
+
+        /* Process Parameter Set */
+        abstract boolean process(EAPOL_MKPDU mkpdu, EAPOL_MKPDU_ParameterSet parameterSet);
+    }
+
+    /* Basic Parameter Set Processor */
+    class BasicParameterSetProcessor extends ParameterSetProcessor {
+
+        MacSecKaY kaY;
+        MkaParticipant participant;
+
+        BasicParameterSetProcessor(MacSecKaY kaY, MkaParticipant participant) {
+            super(kaY, participant);
+            this.kaY = kay;
+            this.participant = participant;
+        }
+
+        @Override
+        public boolean process(EAPOL_MKPDU mkpdu, EAPOL_MKPDU_ParameterSet parameterSet) {
+            EAPOL_MKPDU_BasicParameterSet bps = (EAPOL_MKPDU_BasicParameterSet) parameterSet;
+            byte[] mi = bps.getActorMI();
+            int mn = bps.getActorMN();
+
+            /* MKPDU received from new participant then populate in Potential Peer List */
+            MkaPeer livePeer = participant.isLivePeer(mi);
+            MkaPeer potentialPeer = participant.isPotentialPeer(mi);
+            if ((livePeer == null) && (potentialPeer == null)) {
+                MkaPeer peer = new MkaPeer(bps.getSci(), mi, mn);
+                peer.setKeyServer(bps.getKeyServer());
+                peer.setKeyServerPriority(bps.getKeyServerPriority());
+                peer.setMacSecDesired(bps.getMacSecDesired());
+                peer.setMacSecCapability(bps.getMacSecCapacity());
+                peer.setTimestamp(System.currentTimeMillis());
+                participant.addPotentialPeer(peer);
+            } else {
+                /* Already exiting actor, update details.*/
+                MkaPeer currentPeer = (livePeer != null) ? livePeer : potentialPeer;
+                if (currentPeer.mn() < mn) {
+                    currentPeer.setMN(mn);
+                    currentPeer.setKeyServer(bps.getKeyServer());
+                    currentPeer.setKeyServerPriority(bps.getKeyServerPriority());
+                    currentPeer.setMacSecDesired(bps.getMacSecDesired());
+                    currentPeer.setMacSecCapability(bps.getMacSecCapacity());
+                    currentPeer.setTimestamp(System.currentTimeMillis());
+                    /* (livePeer != null) ? participant.updateLivePeer(currentPeer)
+                                : participant.updatePotentialPeer(currentPeer); */
+                }
+            }
+            /* Myself in peer's peer list and peer is in Potential Peer List */
+            // if ((potentialPeer != null) &&
+            //         (participant.myselfPeerOfRemotePeer(eapolMkpdu.getPeerListParameterSet()) == true)) {
+            //     /* Update Live Peer list : */
+            //     participant.addLivePeer(participant.removePotentialPeer(mi));
+            // }
+            return true;
+        }
+    }
+
+    /* Live Peer List Parameter Set Processor. */
+    class LivePeerListProcessor extends ParameterSetProcessor {
+
+        MacSecKaY kaY;
+        MkaParticipant participant;
+
+        LivePeerListProcessor(MacSecKaY kaY, MkaParticipant participant) {
+            super(kaY, participant);
+            this.kaY = kay;
+            this.participant = participant;
+        }
+
+        @Override
+        boolean process(EAPOL_MKPDU mkpdu, EAPOL_MKPDU_ParameterSet parameterSet) {
+                /* Actor basic details. */
+            EAPOL_MKPDU_BasicParameterSet bps = mkpdu.getBasicParameterSet();
+            final byte[] actorMI = bps.getActorMI();
+            int actorMN = bps.getActorMN();
+                /* Process each peer details in Live Parameter Set */
+            EAPOL_MKPDU_PeerListParameterSet lplPS = (EAPOL_MKPDU_PeerListParameterSet) parameterSet;
+            List<MemberDetails> lplMembers = lplPS.getMembers();
+            lplMembers.forEach(m -> {
+                byte[] mID = m.getMemberID();
+                int mn = m.getMessageNo();
+                    /* Live peer is myself. */
+                if (Arrays.equals(mID, participant.mi)) {
+                        /* Check peer is in Potential Peer list, if so update to Live Peers list.  */
+                    if (participant.isPotentialPeer(actorMI) != null) {
+                        MkaPeer peer = participant.removePotentialPeer(actorMI);
+                        participant.addLivePeer(peer);
+                        participant.createReceiveSC(peer.getSci());
+                        secY.createReceiveSA(port.number().name(), peer.getSci(), txSC.getNextAvailableAN(),
+                                1, 1);
+                    }
+                    return;
+                }
+                    /* Update if peer is in live list, otherwise add new in potential list. */
+                MkaPeer peer = participant.isLivePeer(mID);
+                if (peer != null) {
+                    peer.setMN(mn);
+                    peer.setTimestamp(System.currentTimeMillis());
+                } else {
+                    MkaPeer potentialPeer = new MkaPeer(peer.getSci(), mID, mn);
+                    potentialPeer.setTimestamp(System.currentTimeMillis());
+                    participant.addPotentialPeer(potentialPeer);
+                }
+            });
+            return false;
+        }
+    }
+
+    Map<Byte, ParameterSetProcessor> psProcessors = new LinkedHashMap<>();
+
+    /* Hello Timer Executor */
+    ScheduledExecutorService executorService;
+
+    /* PAE Group Address. */
+    public static final byte[] HELLO_MKPDU_SEND_ADDRESS = {(byte) 0x01, (byte) 0x80, (byte) 0xC2,
+            (byte) 0x00, (byte) 0x00, (byte) 0x03};
+
+    /* Constructor using Key & KeyName */
+    MkaParticipant(StateMachine sm, PacketService ps,
+                   ScheduledExecutorService executerService, MacSecKaY kay, MacSecSecY secY, byte[] key,
+                   byte[] keyName, SCI actorSci, DeviceId deviceId, Port port) {
+
+        /* Sanitize. Evaluate CAK & CKN */
+        if ((key == null) || (keyName == null)) {
+            /* TODO : */
+        }
+        if ((kay.defaultAlgorithmAgility().getCakLength() != key.length)) {
+            /* TODO : */
+        }
+
+        /* Initialize KaY, SM, Packet Service etc. */
+        this.kay = kay;
+        this.secY = secY;
+        this.sm = sm;
+        this.ps = ps;
+        this.executorService = executerService;
+
+        /* Default MACSec RX/TX Protection Features. */
+        macSecCapability = MacSecCipherSuit.MacSecCapabilities.INTEGRITY_AND_CONFIDENTIALITY;
+        macSecDesired = true;
+
+        /* KeyStore, SC & SA details */
+        keyStore = new KeyStore();
+        this.actorSci = actorSci;
+        this.port = port;
+        txSC = new TransmitSC(actorSci);
+        secY.createTransmitSC(port.number().name(), actorSci);
+
+        /* Key details update. */
+        cak = key;
+        ckn = keyName;
+
+        /* Clear Member ID */
+        resetMI();
+
+        /* Initialize Key Server Details. */
+        this.deviceId = deviceId;
+        latestKI = mi;
+        latestKN = 1; /* TODO: Starting KeyNumber Configurable. */
+        latestTX = true;
+        latestRX = true;
+        latestAN = 0x01; /* TODO: Association Number should be initialized using standard method. */
+        oldKI = emptyKI; /* Cleared old Member ID. */
+        oldKN = 0;
+        oldTX = latestTX;
+        oldRX = latestRX;
+        oldAN = 0X00; /* Cleared old AN. */
+
+        /* Derive KEK, ICK from CAK, CKN */
+        MacSecAlgorithmAgility algorithmAgility = kay.defaultAlgorithmAgility();
+        kek = algorithmAgility.generateKek(cak, ckn);
+        ick = algorithmAgility.generateIck(cak, ckn);
+
+        /* Derive SAK from CAK */
+        byte[] ksNonce = new byte[16];
+        SecureRandom randomGenerator = new SecureRandom();
+        randomGenerator.nextBytes(ksNonce);
+        sak = algorithmAgility.generateSak(cak, ksNonce, mi, 1);
+        keyStore.createKey(latestKI, sak);
+
+        /* Initialize Parameter Creators. */
+        psCreators.put(EAPOL_MKPDU_ParameterSet.PARAMETERSET_TYPE_BASIC,
+                new BasicParameterSetCreator(kay, this));
+        psCreators.put(EAPOL_MKPDU_ParameterSet.PARAMETERSET_TYPE_LIVE_PEER_LIST,
+                new LivePeerListCreator(kay, this));
+        psCreators.put(EAPOL_MKPDU_ParameterSet.PARAMETERSET_TYPE_POTENTIAL_PEER_LIST,
+                new PotentialPeerListCreator(kay, this));
+        psCreators.put(EAPOL_MKPDU_ParameterSet.PARAMETERSET_TYPE_MACSEC_SAK_USE,
+                new MacSecUseCreator(kay, this));
+        psCreators.put(EAPOL_MKPDU_ParameterSet.PARAMETERSET_TYPE_DISTRIBUTED_SAK,
+                new DistributedSakCreator(kay, this));
+        /* TODO: Other parameter sets filling. */
+        psCreators.put(EAPOL_MKPDU_ParameterSet.PARAMETERSET_TYPE_ICV_INDICATOR,
+                new IcvCreator(kay, this));
+
+        /* Initialize Parameter Processors. */
+        psProcessors.put(EAPOL_MKPDU_ParameterSet.PARAMETERSET_TYPE_BASIC,
+                new BasicParameterSetProcessor(kay, this));
+        psProcessors.put(EAPOL_MKPDU_ParameterSet.PARAMETERSET_TYPE_LIVE_PEER_LIST,
+                new LivePeerListProcessor(kay, this));
+        /* TODO : Other parameter set processing. */
+    }
+
+    /* Actor SCI */
+    public SCI actorSci() {
+        return actorSci;
+    }
+    public void setActorSci(SCI sci) {
+        this.actorSci = sci;
+    }
+
+    /* Outbound device details for packet transmission. */
+    public DeviceId deviceId() {
+        return deviceId;
+    }
+    public void setDeviceId(DeviceId deviceId) {
+        this.deviceId = deviceId;
+    }
+
+    /* Outbound port details for packet transmission. */
+    public Port port() {
+        return port;
+    }
+    public void setPort(Port port) {
+        this.port = port;
+    }
+
+    /* Process received EAPOL-MKPDU */
+    public void receive(EAPOL_MKPDU mkpdu) {
+        /* Process Various Parameter Sets */
+        ((LinkedHashMap<Byte, ParameterSetProcessor>) psProcessors).forEach((key, psp) -> {
+            EAPOL_MKPDU_ParameterSet ps = mkpdu.getParameterSet(key);
+            if ((ps != null) && (psp != null)) {
+                psp.process(mkpdu, ps);
+            }
+        });
+        return;
+    }
+
+    /* Start ticking heartbeat. ie enables Timers. */
+    public void startTicking() {
+        /* Initialize Hello Timer.  */
+        class WorkerThread implements Runnable {
+            MkaParticipant participant;
+            WorkerThread(MkaParticipant participant) {
+                this.participant = participant;
+            }
+            public void run() {
+                /* TODO: Make sure my lifespan exists. */
+                /* TODO: Clear expired Live Peers. */
+                /* TODO : Clear expired Potential Peers. */
+                /* TODO: Regenerate SAK if needed snd send it. */
+                /* Build & Send MKPDU */
+                log.info("Hello-Timer : Transmitting EAPOL-MKPDU....");
+                participant.transmitMkpdu();
+                log.info("Done.");
+            }
+        }
+        executorService.scheduleAtFixedRate(new WorkerThread(this), 5, 5, TimeUnit.SECONDS);
+    }
+
+    /* Building & Sending EAPOL-MKPDU. Hello Timer's routine. */
+    public void transmitMkpdu() {
+
+        /* EAPOL-MKA packet building & populating Parameter Sets*/
+        EAPOL_MKPDU mkpdu = new EAPOL_MKPDU();
+        for (Map.Entry<Byte, ParameterSetCreator> entry : psCreators.entrySet()) {
+            ParameterSetCreator c = entry.getValue();
+            mkpdu.addParameterSet(entry.getKey(), c.buildParameterSet());
+        }
+
+        /* EAPOL Header Filling */
+        EAPOL eapol = new EAPOL();
+        eapol.setEapolType(EAPOL.EAPOL_MKA);
+        eapol.setPacketLength(mkpdu.packetLength());
+        eapol.setPayload(mkpdu);
+
+        /* Ethernet Header Filling. */
+        Ethernet ethernet = new Ethernet();
+        ethernet.setDestinationMACAddress(HELLO_MKPDU_SEND_ADDRESS);
+        ethernet.setSourceMACAddress(this.actorSci().address());
+        ethernet.setEtherType(EthType.EtherType.EAPOL.ethType().toShort());
+        ethernet.setVlanID(Ethernet.VLAN_UNTAGGED);
+        ethernet.setPayload(eapol);
+        ethernet.setPad(false);
+
+        /* ICV Calculation and Populating */
+        ByteBuffer frame = ByteBuffer.wrap(ethernet.serialize());
+        byte[] frameWoIcv = new byte[frame.capacity() -
+                kay.defaultAlgorithmAgility().getIcvLength() / 8];
+        frame.get(frameWoIcv);
+        byte[] icv = kay.defaultAlgorithmAgility().generateIcv(ick, frameWoIcv);
+        frame.position(frame.capacity() -
+                kay.defaultAlgorithmAgility().getIcvLength() / 8);
+        frame.put(icv);
+
+        /* Send packet to supplicant.  */
+        TrafficTreatment treatment = DefaultTrafficTreatment.builder().setOutput(port.number()).build();
+        OutboundPacket packet = new DefaultOutboundPacket(deviceId, treatment, frame);
+        ps.emit(packet);
+        log.info("Flushed out Hello-Timer EAPOL-MKA PDU.");
+    }
+
+    /* CP state machine KaY interactions. IEEE 802.1X 2010 Figure 12-2. */
+    public void createReceiveSC(SCI sci) {
+        if (sci == null) {
+            log.info("Invalid SCI provided for SC creation.");
+            return;
+        }
+        rxSCList.add(new ReceiveSC(sci));
+        secY.createReceiveSC(port.number().name(), sci);
+    }
+
+    /* Creating Receive & Transmit SAs /*/
+    public void createSAs(byte[] keyIdentifier) {
+        List<SecureAssociation> sas = keyStore.retrieveSAs(keyIdentifier);
+        if (sas != null && sas.size() > 0) {
+            log.error("Improper keystore stage : {}", keyIdentifier);
+            return;
+        }
+
+        /* Create Transmit SAs */
+        SecureAssociation sa = txSC.createSA(txSC.getNextAvailableAN(), 1);
+        keyStore.storeSA(keyIdentifier, sa);
+        secY.createTransmitSA(port.number().name(), txSC.sci(), sa.an(), 1);
+        keyStore.transmits(keyIdentifier, true);
+
+        /* Create Receive SAs */
+        rxSCList.stream().forEach(sc -> {
+            SecureAssociation rxSA = sc.createSA(sc.getNextAvailableAN(), 1, 1);
+            keyStore.storeSA(keyIdentifier, rxSA);
+            secY.createReceiveSA(port.number().name(), sc.sci(), rxSA.an(), 1, 1);
+        });
+        keyStore.receives(keyIdentifier, true);
+    }
+
+    /* Deleting SAs associated with certain key. */
+    public void deleteSAs(byte[] keyIdentifier) {
+        List<SecureAssociation> saList = keyStore.retrieveSAs(keyIdentifier);
+        if (saList == null) {
+            log.error("Failed looking up key store. Key ID : {}", keyIdentifier);
+            return;
+        }
+        saList.stream().forEach(sa -> {
+            SecureConnection sc = sa.parent();
+            if (sc.type() == SCType.GENERATION) {
+                secY.deleteTransmitSA(port.number().name(), sc.sci(), sa.an());
+            } else {
+                secY.deleteReceiveSA(port.number().name(), sc.sci(), sa.an());
+            }
+            sc.detachSA(sa);
+        });
+        keyStore.transmits(keyIdentifier, false);
+        keyStore.receives(keyIdentifier, false);
+    }
+
+    public void enableReceiveSAs(byte[] keyIdentifier) {
+        List<SecureAssociation> saList = keyStore.retrieveSAs(keyIdentifier);
+        if (saList == null) {
+            log.error("Failed looking up key store. Key ID : {}", keyIdentifier);
+            return;
+        }
+
+        // Iterate by filtering receive SAs in store and enable it
+        CpStateMachine cpSM = kay.lookupCPStateMachineByID(sm.sessionId());
+        saList.stream().filter(sa -> sa.parent().type() == SCType.GENERATION)
+                .forEach(sa -> {
+                    SecureConnection sc = sa.parent();
+                    sa.setInUse(true);
+                    secY.enableReceiveSA(port.number().name(), sc.sci(), sa.an());
+                    cpSM.setUsingReceiveSAs(true);
+                    cpSM.stepStateMachine();
+                });
+        return;
+    }
+
+    public void enableTransmitSA(byte[] keyIdentifier) {
+        List<SecureAssociation> saList = keyStore.retrieveSAs(keyIdentifier);
+        if (saList == null) {
+            log.error("Failed looking up key store. Key ID : {}", keyIdentifier);
+            return;
+        }
+
+        // Iterate by filtering receive SAs in store and enable it
+        CpStateMachine cpSM = kay.lookupCPStateMachineByID(sm.sessionId());
+        saList.stream().filter(sa -> sa.parent().type() == SCType.GENERATION)
+                .forEach(sa -> {
+                    SecureConnection sc = sa.parent();
+                    sa.setInUse(true);
+                    secY.enableTransmitSA(port.number().name(), sc.sci(), sa.an());
+                    cpSM.setUsingTransmitSA(true);
+                    cpSM.stepStateMachine();
+                });
+        return;
+    }
+
+    public void setOldSAAttributes(byte[] ki, byte an, boolean tx, boolean rx) {
+        oldKI = (ki == null) ? emptyKI : ki;
+        oldAN = an; oldTX = tx; oldRX = rx;
+    }
+
+    public void setLatestSAAttributes(byte[] ki, byte an, boolean tx, boolean rx) {
+        latestKI = (ki == null) ? emptyKI : ki;
+        latestAN = an; latestTX = tx; latestRX = rx;
+    }
+
+    /* Peer details storing. */
+    protected class MkaPeer {
+        /* Basic Parameter Set details.*/
+        SCI sci;
+        private byte[] mi;
+        private int mn;
+        private boolean isKeyServer;
+        private byte keyServerPriority;
+        private boolean macSecDesired;
+        private byte macSecCapbility;
+        /* Expire time details */
+        private long expireTimestamp;
+        MkaPeer(SCI sci, byte[] mi, int mn) {
+            this.sci = sci;
+            this.mi = mi;
+            this.mn = mn;
+        }
+        /* Member Identifier */
+        public byte[] mi() {
+            return mi;
+        }
+        /* Message Number */
+        public int mn() {
+            return mn;
+        }
+        public void setMN(int mn) {
+            this.mn = mn;
+        }
+        /* Key Server Status */
+        public boolean keyServer() {
+            return isKeyServer;
+        }
+        public void setKeyServer(boolean keyServer) {
+            this.isKeyServer = keyServer;
+        }
+        /* Key Server Priority */
+        public byte keyServerPriority() {
+            return keyServerPriority;
+        }
+        public void setKeyServerPriority(byte keyServerPriority) {
+            this.keyServerPriority = keyServerPriority;
+        }
+        /* MACSec desirability */
+        public boolean macSecDesired() {
+            return macSecDesired;
+        }
+        public void setMacSecDesired(boolean macSecDesired) {
+            this.macSecDesired = macSecDesired;
+        }
+        /* MACSec capability */
+        public byte macSecCapability() {
+            return macSecCapbility;
+        }
+        public void setMacSecCapability(byte macSecCapbility) {
+            this.macSecCapbility = macSecCapbility;
+        }
+        /* Expire time details. */
+        public void setTimestamp(long timestamp) {
+            expireTimestamp = timestamp;
+        }
+        public long getTimestamp() {
+            return expireTimestamp;
+        }
+        public SCI getSci() {
+            return sci;
+        }
+    }
+
+    /* Live peer or not ? */
+    protected MkaPeer isLivePeer(byte[] mi) {
+        MkaPeer p = livePeers.stream()
+                .filter((q) -> Arrays.equals(q.mi(), mi))
+                .findAny()
+                .orElse(null);
+        return p;
+    }
+
+    /* Live peers at a time. */
+    protected List<MkaPeer> allLivePeers() {
+        return livePeers;
+    }
+
+    /* Potential peer or not ? */
+    protected MkaPeer isPotentialPeer(byte[] mi) {
+        MkaPeer p = potentialPeers.stream()
+                .filter((q) -> Arrays.equals(q.mi(), mi))
+                .findAny()
+                .orElse(null);
+        // if( p != null ) {
+        //     /* Update MN if new. */
+        //     if (p.getMN() <  mn) p.setMN(mn);
+        // }
+        return p;
+    }
+
+    /* All Potential Peers at a time. */
+    protected List<MkaPeer> allPotentialPeers() {
+        return potentialPeers;
+    }
+
+    /* Populate Potential Peer */
+    protected void addPotentialPeer(MkaPeer p) {
+        if (p != null && (isPotentialPeer(p.mi()) == null)) {
+            potentialPeers.add(p);
+        }
+        return;
+    }
+
+    /* Remove Potential Peer */
+    protected MkaPeer removePotentialPeer(byte[] mi) {
+        MkaPeer p = potentialPeers.stream()
+                .filter((q) -> Arrays.equals(q.mi(), (mi)))
+                .findAny()
+                .orElse(null);
+        if (p != null) {
+            potentialPeers.remove(p);
+        }
+        return p;
+    }
+
+    /* Populate Live Peer */
+    protected void addLivePeer(MkaPeer p) {
+        if (p != null && (isLivePeer(p.mi()) == null)) {
+            livePeers.add(p);
+        }
+        return;
+    }
+
+    /* Check myself present in remote peer's peer list */
+    protected boolean myselfPeerOfRemotePeer(EAPOL_MKPDU_PeerListParameterSet peerList) {
+        /* Handle Non-Peer List cases. */
+        if (peerList == null) {
+            return false;
+        }
+        if (peerList.getParameterSetType() != EAPOL_MKPDU_PeerListParameterSet.PEERLIST_TYPE_LIVE) {
+            return false;
+        }
+        return peerList.memberExists(mi);
+    }
+
+    /* Resets MI & MN */
+    protected void resetMI() {
+        SecureRandom random = new SecureRandom();
+        mi = new byte[EAPOL_MKPDU_ParameterSet.FIELD_MI_LENGTH];
+        random.nextBytes(mi);
+        /* TODO : Check IEEE 802.1X and finalize. */
+        mn = 1;
+    }
+
+    /* Key Server Priority */
+    protected byte actorPriority() {
+        return actorPriority;
+    }
+
+    /* Key Server MI */
+    protected byte[] actorMI() {
+        return mi;
+    }
+
+    /* Key Server MN */
+    protected int actorMN() {
+        return mn;
+    }
+
+    /* Retrieve and update Key Server MN */
+    protected int retrieveAndUpdateMN() {
+        int currentMN = mn;
+        /* Update MN for each Tx. */
+        mn += 1;
+        return currentMN;
+    }
+
+    /* Various Getters & Setters. */
+
+    /* Various Keys. */
+    protected byte[] cak() {
+        return cak;
+    }
+
+    protected byte[] ckn() {
+        return ckn;
+    }
+
+    protected byte[] sak() {
+        return sak;
+    }
+
+    protected byte[] ick() {
+        return ick;
+    }
+
+    protected byte[] kek() {
+        return kek;
+    }
+
+    /* Current Key Server Key Number */
+    protected int latestKN() {
+        return latestKN;
+    }
+
+    /* Current Key Server Identifier */
+    protected byte[] latestKI() {
+        return latestKI;
+    }
+
+    /* Current Key Server Association Number */
+    protected byte latestAN() {
+        return latestAN;
+    }
+
+    /* Current Key Server used for TX protection ? */
+    protected boolean latestTX() {
+        return latestTX;
+    }
+
+    /* Current Key Server used for RX protection ? */
+    protected boolean latestRX() {
+        return latestRX;
+    }
+
+    /* Old Key Server Identifier */
+    protected byte[] oldKI() {
+        return oldKI;
+    }
+
+    /* Old Key Server Key Number */
+    protected int oldKN() {
+        return oldKN;
+    }
+
+    /* Old Key Server Association Number */
+    protected byte oldAN() {
+        return oldAN;
+    }
+
+    /* Old Key Server used for TX protection ? */
+    protected boolean oldTX() {
+        return oldTX;
+    }
+
+    /* Old Key Server used for RX protection ? */
+    protected boolean oldRX() {
+        return oldRX;
+    }
+}
diff --git a/src/main/java/org/opencord/aaa/PortBasedRadiusCommunicator.java b/src/main/java/org/opencord/aaa/PortBasedRadiusCommunicator.java
index cf53b68..9210ad0 100755
--- a/src/main/java/org/opencord/aaa/PortBasedRadiusCommunicator.java
+++ b/src/main/java/org/opencord/aaa/PortBasedRadiusCommunicator.java
@@ -49,8 +49,6 @@
 
 import org.slf4j.Logger;
 
-import com.google.common.collect.Maps;
-
 import static org.onosproject.net.packet.PacketPriority.CONTROL;
 import static org.slf4j.LoggerFactory.getLogger;
 
@@ -58,6 +56,7 @@
 import java.nio.ByteBuffer;
 import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
 
 /**
  * Handles communication with the RADIUS server through ports
@@ -125,7 +124,8 @@
         this.pktCustomizer = pktCustomizer;
         this.aaaManager = aaaManager;
 
-        ipToSnMap = Maps.newConcurrentMap();
+        //ipToSnMap = Maps.newConcurrentMap();
+        ipToSnMap = new ConcurrentHashMap<>();
         mastershipService.addListener(changeListener);
         deviceService.addListener(deviceListener);
 
diff --git a/src/main/java/org/opencord/aaa/StateMachine.java b/src/main/java/org/opencord/aaa/StateMachine.java
index 6795c43..d4c7904 100644
--- a/src/main/java/org/opencord/aaa/StateMachine.java
+++ b/src/main/java/org/opencord/aaa/StateMachine.java
@@ -18,6 +18,7 @@
 package org.opencord.aaa;
 
 import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
 
 import org.onlab.packet.MacAddress;
 import org.onlab.packet.VlanId;
@@ -27,8 +28,6 @@
 
 import org.slf4j.Logger;
 
-import com.google.common.collect.Maps;
-
 import static org.slf4j.LoggerFactory.getLogger;
 
 /**
@@ -118,8 +117,10 @@
     private static Map<Integer, StateMachine> identifierMap;
 
     public static void initializeMaps() {
-        sessionIdMap = Maps.newConcurrentMap();
-        identifierMap = Maps.newConcurrentMap();
+        //sessionIdMap = Maps.newConcurrentMap();
+        sessionIdMap =  new ConcurrentHashMap<>();
+        //identifierMap = Maps.newConcurrentMap();
+        identifierMap = new ConcurrentHashMap<>();
         identifier = -1;
     }