From 79e3de0967fdd3e41805196d9c3f24b22978ca84 Mon Sep 17 00:00:00 2001
From: Lionel Gauthier <lionel.gauthier@eurecom.fr>
Date: Thu, 28 May 2015 12:38:52 +0000
Subject: [PATCH] OPc in database (computed by HSS if OP provided)

git-svn-id: http://svn.eurecom.fr/openair4G/trunk@7476 818b1a75-f10b-46b9-bf7c-635c3b92a50f
---
 openair-cn/OPENAIRHSS/auc/auc.h             |  32 ++--
 openair-cn/OPENAIRHSS/auc/fx.c              | 125 +++++++-------
 openair-cn/OPENAIRHSS/auc/kdf.c             |  22 +--
 openair-cn/OPENAIRHSS/auc/random.c          |  13 +-
 openair-cn/OPENAIRHSS/auc/sequence_number.c |  10 +-
 openair-cn/OPENAIRHSS/conf/hss.conf.in      |   1 -
 openair-cn/OPENAIRHSS/db/db_connector.c     | 124 +++++++++++++-
 openair-cn/OPENAIRHSS/db/db_proto.h         |  26 ++-
 openair-cn/OPENAIRHSS/db/oai_db.sql         |  36 ++--
 openair-cn/OPENAIRHSS/db/pft_db.sql         | 173 ++++++++++++++++++++
 openair-cn/OPENAIRHSS/hss_main.c            |   5 +
 openair-cn/OPENAIRHSS/s6a/s6a_auth_info.c   |   4 +-
 openair-cn/OPENAIRHSS/utils/hss_config.c    |  70 ++++----
 openair-cn/OPENAIRHSS/utils/hss_config.h    |   6 +-
 openair-cn/OPENAIRHSS/utils/hss_parser.y    |   8 +-
 openair-cn/OPENAIRHSS/utils/hss_scanner.l   |   2 +-
 16 files changed, 468 insertions(+), 189 deletions(-)
 create mode 100644 openair-cn/OPENAIRHSS/db/pft_db.sql

diff --git a/openair-cn/OPENAIRHSS/auc/auc.h b/openair-cn/OPENAIRHSS/auc/auc.h
index 81d7876cec..00ca2b9dc7 100644
--- a/openair-cn/OPENAIRHSS/auc/auc.h
+++ b/openair-cn/OPENAIRHSS/auc/auc.h
@@ -50,7 +50,7 @@ extern uint8_t opc[16];
 typedef mpz_t random_t;
 typedef mpz_t sqn_t;
 
-typedef uint8_t u8;
+typedef uint8_t uint8_t;
 
 typedef struct {
   uint8_t rand[16];
@@ -60,8 +60,8 @@ typedef struct {
   uint8_t kasme[32];
 } auc_vector_t;
 
-void RijndaelKeySchedule(const u8 const key[16]);
-void RijndaelEncrypt(const u8 const in[16], u8 out[16]);
+void RijndaelKeySchedule(const uint8_t const key[16]);
+void RijndaelEncrypt(const uint8_t const in[16], uint8_t out[16]);
 
 /* Sequence number functions */
 struct sqn_ue_s;
@@ -77,19 +77,21 @@ struct random_state_s;
 void random_init(void);
 void generate_random(uint8_t *random, ssize_t length);
 
-void SetOP(char *opP);
+//void SetOP(char *opP);
 
-void f1 ( const u8 const k[16], const u8 const rand[16], const u8 const sqn[6], const u8 const amf[2],
-          u8 mac_a[8] );
-void f1star( const u8 const k[16], const u8 const rand[16], const u8 const sqn[6], const u8 const amf[2],
-             u8 mac_s[8] );
-void f2345 ( const u8 const k[16], const u8 const rand[16],
-             u8 res[8], u8 ck[16], u8 ik[16], u8 ak[6] );
-void f5star( const u8 const k[16], const u8 const rand[16],
-             u8 ak[6] );
+void ComputeOPc( const uint8_t const kP[16], const uint8_t const opP[16], uint8_t opcP[16] );
 
-void generate_autn(const u8 const sqn[6], const u8 const ak[6], const u8 const amf[2], const u8 const mac_a[8], u8 autn[16]);
-int generate_vector(uint64_t imsi, uint8_t key[16], uint8_t plmn[3],
+void f1 ( const uint8_t const kP[16],const uint8_t const k[16], const uint8_t const rand[16], const uint8_t const sqn[6], const uint8_t const amf[2],
+          uint8_t mac_a[8] );
+void f1star( const uint8_t const kP[16],const uint8_t const k[16], const uint8_t const rand[16], const uint8_t const sqn[6], const uint8_t const amf[2],
+             uint8_t mac_s[8] );
+void f2345 ( const uint8_t const kP[16],const uint8_t const k[16], const uint8_t const rand[16],
+             uint8_t res[8], uint8_t ck[16], uint8_t ik[16], uint8_t ak[6] );
+void f5star( const uint8_t const kP[16],const uint8_t const k[16], const uint8_t const rand[16],
+             uint8_t ak[6] );
+
+void generate_autn(const uint8_t const sqn[6], const uint8_t const ak[6], const uint8_t const amf[2], const uint8_t const mac_a[8], uint8_t autn[16]);
+int generate_vector(const uint8_t const opc[16], uint64_t imsi, uint8_t key[16], uint8_t plmn[3],
                     uint8_t sqn[6], auc_vector_t *vector);
 
 void kdf(uint8_t *key, uint16_t key_len, uint8_t *s, uint16_t s_len, uint8_t *out,
@@ -98,7 +100,7 @@ void kdf(uint8_t *key, uint16_t key_len, uint8_t *s, uint16_t s_len, uint8_t *ou
 void derive_kasme(uint8_t ck[16], uint8_t ik[16], uint8_t plmn[3], uint8_t sqn[6],
                   uint8_t ak[6], uint8_t kasme[32]);
 
-uint8_t *sqn_ms_derive(uint8_t *key, uint8_t *auts, uint8_t *rand);
+uint8_t *sqn_ms_derive(const uint8_t const opc[16], uint8_t *key, uint8_t *auts, uint8_t *rand);
 
 static inline void print_buffer(const char *prefix, uint8_t *buffer, int length)
 {
diff --git a/openair-cn/OPENAIRHSS/auc/fx.c b/openair-cn/OPENAIRHSS/auc/fx.c
index 1e21fe80c5..446599a86d 100644
--- a/openair-cn/OPENAIRHSS/auc/fx.c
+++ b/openair-cn/OPENAIRHSS/auc/fx.c
@@ -26,36 +26,16 @@
 extern hss_config_t hss_config;
 
 /*--------- Operator Variant Algorithm Configuration Field --------*/
-/*------- Insert your value of OP here -------*/
-extern uint8_t opc[16];
-extern uint8_t op[16];
+
 
 /*--------------------------- prototypes --------------------------*/
-void ComputeOPc( u8 opP[16] );
 
-void SetOP(char *opP)
-{
-    int ret = sscanf(opP,
-                 "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
-                 (unsigned int*)&op[0],(unsigned int*)&op[1],
-                 (unsigned int*)&op[2],(unsigned int*)&op[3],
-                 (unsigned int*)&op[4],(unsigned int*)&op[5],
-                 (unsigned int*)&op[6],(unsigned int*)&op[7],
-                 (unsigned int*)&op[8],(unsigned int*)&op[9],
-                 (unsigned int*)&op[10],(unsigned int*)&op[11],
-                 (unsigned int*)&op[12],(unsigned int*)&op[13],
-                 (unsigned int*)&op[14],(unsigned int*)&op[15]);
-    if (ret != 16) {
-      fprintf(stderr,
-              "Error in operator key\n");
-      abort();
-    }
-    printf("SetOP: OP : %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X\n",
-		  op[0],op[1],op[2],op[3],op[4],op[5],op[6],op[7],
-		  op[8],op[9],op[10],op[11],op[12],op[13],op[14],op[15]);
-}
 
-void generate_autn(const u8 const sqn[6], const u8 const ak[6], const u8 const amf[2], const u8 const mac_a[8], u8 autn[16])
+/*-------------------------------------------------------------------
+ *
+ *-------------------------------------------------------------------
+ *-----------------------------------------------------------------*/
+void generate_autn(const uint8_t const sqn[6], const uint8_t const ak[6], const uint8_t const amf[2], const uint8_t const mac_a[8], uint8_t autn[16])
 {
   int i;
 
@@ -76,19 +56,20 @@ void generate_autn(const u8 const sqn[6], const u8 const ak[6], const u8 const a
  * field AMF.
  *
  *-----------------------------------------------------------------*/
-void f1 ( const u8 const k[16], const u8 const _rand[16], const u8 const sqn[6], const u8 const amf[2],
-          u8 mac_a[8] )
+void f1 ( const uint8_t const opc[16], const uint8_t const k[16], const uint8_t const _rand[16], const uint8_t const sqn[6], const uint8_t const amf[2],
+          uint8_t mac_a[8] )
 {
-  u8 temp[16];
-  u8 in1[16];
-  u8 out1[16];
-  u8 rijndaelInput[16];
-  u8 i;
+  uint8_t temp[16];
+  uint8_t in1[16];
+  uint8_t out1[16];
+  uint8_t rijndaelInput[16];
+  uint8_t i;
   RijndaelKeySchedule( k );
-  if (hss_config.valid_opc == 0) {
+  /*
+  if (hss_config.valid_op > 0) {
 	SetOP(hss_config.operator_key);
     ComputeOPc( opc );
-  }
+  }*/
 
   for (i=0; i<16; i++)
     rijndaelInput[i] = _rand[i] ^ opc[i];
@@ -133,18 +114,19 @@ void f1 ( const u8 const k[16], const u8 const _rand[16], const u8 const sqn[6],
  * confidentiality key CK, integrity key IK and anonymity key AK.
  *
  *-----------------------------------------------------------------*/
-void f2345 ( const u8 const k[16], const u8 const _rand[16],
-             u8 res[8], u8 ck[16], u8 ik[16], u8 ak[6] )
+void f2345 ( const uint8_t const opc[16], const uint8_t const k[16], const uint8_t const _rand[16],
+             uint8_t res[8], uint8_t ck[16], uint8_t ik[16], uint8_t ak[6] )
 {
-  u8 temp[16];
-  u8 out[16];
-  u8 rijndaelInput[16];
-  u8 i;
+  uint8_t temp[16];
+  uint8_t out[16];
+  uint8_t rijndaelInput[16];
+  uint8_t i;
   RijndaelKeySchedule( k );
-  if (hss_config.valid_opc == 0) {
+
+  /*if (hss_config.valid_op > 0) {
     SetOP(hss_config.operator_key);
     ComputeOPc( opc );
-  }
+  }*/
 
   for (i=0; i<16; i++)
     rijndaelInput[i] = _rand[i] ^ opc[i];
@@ -212,23 +194,23 @@ void f2345 ( const u8 const k[16], const u8 const _rand[16],
  * field AMF.
  *
  *-----------------------------------------------------------------*/
-void f1star( const u8 const k[16], const u8 const _rand[16], const u8 const sqn[6], const u8 const amf[2],
-             u8 mac_s[8] )
+void f1star( const uint8_t const opc[16], const uint8_t const k[16], const uint8_t const _rand[16], const uint8_t const sqn[6], const uint8_t const amf[2],
+             uint8_t mac_s[8] )
 {
-  u8 temp[16];
-  u8 in1[16];
-  u8 out1[16];
-  u8 rijndaelInput[16];
-  u8 i;
+  uint8_t temp[16];
+  uint8_t in1[16];
+  uint8_t out1[16];
+  uint8_t rijndaelInput[16];
+  uint8_t i;
   RijndaelKeySchedule( k );
-  if (hss_config.valid_opc == 0) {
+  /*if (hss_config.valid_opc == 0) {
 	SetOP(hss_config.operator_key);
     ComputeOPc( opc );
   } else {
 	  printf("Using opc:  %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X\n",
 		         opc[0],opc[1],opc[2],opc[3],opc[4],opc[5],opc[6],opc[7],
 		         opc[8],opc[9],opc[10],opc[11],opc[12],opc[13],opc[14],opc[15] );
-  }
+  }*/
 
   for (i=0; i<16; i++)
     rijndaelInput[i] = _rand[i] ^ opc[i];
@@ -273,23 +255,23 @@ void f1star( const u8 const k[16], const u8 const _rand[16], const u8 const sqn[
  * anonymity key AK.
  *
  *-----------------------------------------------------------------*/
-void f5star( const u8 const k[16], const u8 const _rand[16],
-             u8 ak[6] )
+void f5star( const uint8_t const opc[16], const uint8_t const k[16], const uint8_t const _rand[16],
+             uint8_t ak[6] )
 {
-  u8 temp[16];
-  u8 out[16];
-  u8 rijndaelInput[16];
-  u8 i;
+  uint8_t temp[16];
+  uint8_t out[16];
+  uint8_t rijndaelInput[16];
+  uint8_t i;
 
   RijndaelKeySchedule( k );
-  if (hss_config.valid_opc == 0) {
+  /*if (hss_config.valid_opc == 0) {
 	SetOP(hss_config.operator_key);
     ComputeOPc(opc);
   } else {
 	  printf("Using OPc: %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X\n",
 			  opc[0],opc[1],opc[2],opc[3],opc[4],opc[5],opc[6],opc[7],
 			  opc[8],opc[9],opc[10],opc[11],opc[12],opc[13],opc[14],opc[15]);
-  }
+  }*/
 
   for (i=0; i<16; i++)
     rijndaelInput[i] = _rand[i] ^ opc[i];
@@ -315,22 +297,25 @@ void f5star( const u8 const k[16], const u8 const _rand[16],
 } /* end of function f5star */
 
 /*-------------------------------------------------------------------
- * Function to compute OPc from OP and K. Assumes key schedule has
- * already been performed.
+ * Function to compute OPc from OP and K.
  *-----------------------------------------------------------------*/
-void ComputeOPc( u8 opcP[16] )
+void ComputeOPc( const uint8_t const kP[16], const uint8_t const opP[16], uint8_t opcP[16] )
 {
-  u8 i;
-
-  RijndaelEncrypt( op, opcP );
-  printf("Compute opc:\n\tIn:\t%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X\n\tRinj:\t%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X\n",
-		  op[0],op[1],op[2],op[3],op[4],op[5],op[6],op[7],
-		  op[8],op[9],op[10],op[11],op[12],op[13],op[14],op[15],
+  uint8_t i;
+
+  RijndaelKeySchedule( kP );
+  printf("Compute opc:\n\tK:\t%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X\n",
+		  kP[0],kP[1],kP[2],kP[3],kP[4],kP[5],kP[6],kP[7],
+		  kP[8],kP[9],kP[10],kP[11],kP[12],kP[13],kP[14],kP[15]);
+  RijndaelEncrypt( opP, opcP );
+  printf("\tIn:\t%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X\n\tRinj:\t%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X\n",
+		  opP[0],opP[1],opP[2],opP[3],opP[4],opP[5],opP[6],opP[7],
+		  opP[8],opP[9],opP[10],opP[11],opP[12],opP[13],opP[14],opP[15],
 	      opcP[0],opcP[1],opcP[2],opcP[3],opcP[4],opcP[5],opcP[6],opcP[7],
 	      opcP[8],opcP[9],opcP[10],opcP[11],opcP[12],opcP[13],opcP[14],opcP[15] );
 
   for (i=0; i<16; i++)
-	  opcP[i] ^= op[i];
+	  opcP[i] ^= opP[i];
   printf("\tOut:\t%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X\n",
 	         opcP[0],opcP[1],opcP[2],opcP[3],opcP[4],opcP[5],opcP[6],opcP[7],
 	         opcP[8],opcP[9],opcP[10],opcP[11],opcP[12],opcP[13],opcP[14],opcP[15] );
diff --git a/openair-cn/OPENAIRHSS/auc/kdf.c b/openair-cn/OPENAIRHSS/auc/kdf.c
index 437b9d9b31..8c83c9665a 100644
--- a/openair-cn/OPENAIRHSS/auc/kdf.c
+++ b/openair-cn/OPENAIRHSS/auc/kdf.c
@@ -41,15 +41,6 @@
 #define DEBUG_AUC_KDF 1
 extern hss_config_t hss_config;
 
-uint8_t opc[16] = {
-		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
-};
-uint8_t op[16] = {
-		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
-};
-
 /*
  * @param key the input key
  * @param key_len length of the key
@@ -93,9 +84,9 @@ void derive_kasme(uint8_t ck[16], uint8_t ik[16], uint8_t plmn[3], uint8_t sqn[6
   memcpy(&key[0], ck, 16);
   memcpy(&key[16], ik, 16);
 
-  if (hss_config.valid_opc == 0) {
+  /*if (hss_config.valid_opc == 0) {
     SetOP(hss_config.operator_key);
-  }
+  }*/
 
   /* FC */
   s[0] = 0x10;
@@ -137,7 +128,7 @@ void derive_kasme(uint8_t ck[16], uint8_t ik[16], uint8_t plmn[3], uint8_t sqn[6
   kdf(key, 32, s, 14, kasme, 32);
 }
 
-int generate_vector(uint64_t imsi, uint8_t key[16], uint8_t plmn[3],
+int generate_vector(const uint8_t const opc[16], uint64_t imsi, uint8_t key[16], uint8_t plmn[3],
                     uint8_t sqn[6], auc_vector_t *vector)
 {
   /* in E-UTRAN an authentication vector is composed of:
@@ -147,7 +138,8 @@ int generate_vector(uint64_t imsi, uint8_t key[16], uint8_t plmn[3],
    * - KASME
    */
 
-  uint8_t amf[] = { 0x80, 0x00 };
+  //uint8_t amf[] = { 0x80, 0x00 };
+  uint8_t amf[] = { 0x90, 0x01 };
   uint8_t mac_a[8];
   uint8_t ck[16];
   uint8_t ik[16];
@@ -158,14 +150,14 @@ int generate_vector(uint64_t imsi, uint8_t key[16], uint8_t plmn[3],
   }
 
   /* Compute MAC */
-  f1(key, vector->rand, sqn, amf, mac_a);
+  f1(opc, key, vector->rand, sqn, amf, mac_a);
 
   print_buffer("MAC_A   : ", mac_a, 8);
   print_buffer("SQN     : ", sqn, 6);
   print_buffer("RAND    : ", vector->rand, 16);
 
   /* Compute XRES, CK, IK, AK */
-  f2345(key, vector->rand, vector->xres, ck, ik, ak);
+  f2345(opc, key, vector->rand, vector->xres, ck, ik, ak);
   print_buffer("AK      : ", ak, 6);
   print_buffer("CK      : ", ck, 16);
   print_buffer("IK      : ", ik, 16);
diff --git a/openair-cn/OPENAIRHSS/auc/random.c b/openair-cn/OPENAIRHSS/auc/random.c
index ff9d4f9866..5e71b32461 100644
--- a/openair-cn/OPENAIRHSS/auc/random.c
+++ b/openair-cn/OPENAIRHSS/auc/random.c
@@ -35,6 +35,7 @@
 #include <sys/time.h>
 
 #include "auc.h"
+#include "hss_config.h"
 
 typedef struct random_state_s {
   pthread_mutex_t lock;
@@ -42,6 +43,7 @@ typedef struct random_state_s {
 } random_state_t;
 
 random_state_t random_state;
+extern hss_config_t hss_config;
 
 void random_init(void)
 {
@@ -70,13 +72,18 @@ void generate_random(uint8_t *random_p, ssize_t length)
 
   //    mpz_export(random_p, NULL, 1, length, 0, 0, random_nb);
   int i;//r = 0,  mask = 0, shift;
-
-  for (i = 0; i < length; i ++) {
+  if (hss_config.random_bool > 0) {
+    for (i = 0; i < length; i ++) {
     //        if ((i % sizeof(i)) == 0)
     //            r = rand();
     //        shift = 8 * (i % sizeof(i));
     //        mask = 0xFF << shift;
     //        random_p[i] = (r & mask) >> shift;
-    random_p[i] = rand();
+	  random_p[i] = rand();
+    }
+  } else {
+    for (i = 0; i < length; i ++) {
+	  random_p[i] = 0;
+	}
   }
 }
diff --git a/openair-cn/OPENAIRHSS/auc/sequence_number.c b/openair-cn/OPENAIRHSS/auc/sequence_number.c
index 0e8ad2d756..2f53b98be0 100644
--- a/openair-cn/OPENAIRHSS/auc/sequence_number.c
+++ b/openair-cn/OPENAIRHSS/auc/sequence_number.c
@@ -37,7 +37,7 @@
 extern hss_config_t hss_config;
 extern uint8_t op[16];
 
-uint8_t *sqn_ms_derive(uint8_t *key, uint8_t *auts, uint8_t *rand_p)
+uint8_t *sqn_ms_derive(const uint8_t const opc[16], uint8_t *key, uint8_t *auts, uint8_t *rand_p)
 {
   /* AUTS = Conc(SQN MS ) || MAC-S
    * Conc(SQN MS ) = SQN MS ^ f5* (RAND)
@@ -56,12 +56,12 @@ uint8_t *sqn_ms_derive(uint8_t *key, uint8_t *auts, uint8_t *rand_p)
 
   sqn_ms = malloc(SQN_LENGTH_OCTEST);
 
-  if (hss_config.valid_opc == 0) {
+  /*if (hss_config.valid_opc == 0) {
     SetOP(hss_config.operator_key);
-  }
+  }*/
 
   /* Derive AK from key and rand */
-  f5star(key, rand_p, ak);
+  f5star(opc, key, rand_p, ak);
 
   for (i = 0; i < 6; i++) {
     sqn_ms[i] = ak[i] ^ conc_sqn_ms[i];
@@ -74,7 +74,7 @@ uint8_t *sqn_ms_derive(uint8_t *key, uint8_t *auts, uint8_t *rand_p)
   print_buffer("sqn_ms_derive() SQN_MS : ", sqn_ms, 6);
   print_buffer("sqn_ms_derive() MAC_S  : ", mac_s, 8);
 
-  f1star(key, rand_p, sqn_ms, amf, mac_s_computed);
+  f1star(opc, key, rand_p, sqn_ms, amf, mac_s_computed);
 
   print_buffer("MAC_S +: ", mac_s_computed, 8);
 
diff --git a/openair-cn/OPENAIRHSS/conf/hss.conf.in b/openair-cn/OPENAIRHSS/conf/hss.conf.in
index f0732669ad..4c5e4c7d93 100644
--- a/openair-cn/OPENAIRHSS/conf/hss.conf.in
+++ b/openair-cn/OPENAIRHSS/conf/hss.conf.in
@@ -6,7 +6,6 @@ MYSQL_db     = "@MYSQL_db@";
 
 ## HSS options
 OPERATOR_key = "@OPERATOR_key@";
-OPERATOR_ckey = "@OPERATOR_ckey@";
 
 ## Freediameter options
 FD_conf = "@FREEDIAMETER_PATH@/../etc/freeDiameter/hss_fd.conf";
diff --git a/openair-cn/OPENAIRHSS/db/db_connector.c b/openair-cn/OPENAIRHSS/db/db_connector.c
index 7e5fada9c7..6585036b04 100644
--- a/openair-cn/OPENAIRHSS/db/db_connector.c
+++ b/openair-cn/OPENAIRHSS/db/db_connector.c
@@ -42,6 +42,9 @@
 #include "hss_config.h"
 #include "db_proto.h"
 
+extern void ComputeOPc( const uint8_t const kP[16], const uint8_t const opP[16], uint8_t opcP[16] );
+
+
 database_t *db_desc;
 
 static void print_buffer(const char *prefix, uint8_t *buffer, int length)
@@ -519,7 +522,7 @@ int hss_mysql_auth_info(mysql_auth_info_req_t  *auth_info_req,
     return EINVAL;
   }
 
-  sprintf(query, "SELECT `key`,`sqn`,`rand` FROM `users` WHERE `users`.`imsi`=%s ",
+  sprintf(query, "SELECT `key`,`sqn`,`rand`,`OPc` FROM `users` WHERE `users`.`imsi`=%s ",
           auth_info_req->imsi);
 
   DB_DEBUG("Query: %s\n", query);
@@ -539,7 +542,7 @@ int hss_mysql_auth_info(mysql_auth_info_req_t  *auth_info_req,
   pthread_mutex_unlock(&db_desc->db_cs_mutex);
 
   if ((row = mysql_fetch_row(res)) != NULL) {
-    if (row[0] == NULL || row[1] == NULL || row[2] == NULL) {
+    if (row[0] == NULL || row[1] == NULL || row[2] == NULL || row[3] == NULL) {
       ret = EINVAL;
     }
 
@@ -569,6 +572,11 @@ int hss_mysql_auth_info(mysql_auth_info_req_t  *auth_info_req,
       memcpy(auth_info_resp->rand, row[2], RAND_LENGTH);
     }
 
+    if (row[3] != NULL) {
+        print_buffer("OPc: ", (uint8_t*)row[3], KEY_LENGTH);
+        memcpy(auth_info_resp->opc, row[3], KEY_LENGTH);
+    }
+
     mysql_free_result(res);
     mysql_thread_end();
     return ret;
@@ -579,3 +587,115 @@ int hss_mysql_auth_info(mysql_auth_info_req_t  *auth_info_req,
 
   return EINVAL;
 }
+
+int hss_mysql_check_opc_keys(const uint8_t const opP[16])
+{
+  int        ret   = 0;
+  MYSQL_RES *res   = NULL;
+  MYSQL_RES *res2  = NULL;
+  MYSQL_ROW  row;
+  char       query[1000];
+  char       update[1000];
+  uint8_t    k[16];
+  uint8_t    opc[16];
+  int        update_length = 0;
+  int        status        = 0;
+  int        i;
+
+  if (db_desc->db_conn == NULL) {
+	return EINVAL;
+  }
+
+  sprintf(query, "SELECT `imsi`,`key`,`OPc` FROM `users` ");
+
+  DB_DEBUG("Query: %s\n", query);
+
+  pthread_mutex_lock(&db_desc->db_cs_mutex);
+
+  if (mysql_query(db_desc->db_conn, query)) {
+	pthread_mutex_unlock(&db_desc->db_cs_mutex);
+	DB_ERROR("Query execution failed: %s\n",
+			 mysql_error(db_desc->db_conn));
+	mysql_thread_end();
+	return EINVAL;
+  }
+
+  res = mysql_store_result(db_desc->db_conn);
+
+  pthread_mutex_unlock(&db_desc->db_cs_mutex);
+
+
+  while ((row = mysql_fetch_row(res))) {
+    if (row[0] == NULL || row[1] == NULL ) {
+      DB_ERROR("Query execution failed: %s\n",
+    				 mysql_error(db_desc->db_conn));
+      ret = EINVAL;
+    } else {
+	  if (row[0] != NULL) {
+	    printf("IMSI: %s", (uint8_t*)row[0]);
+	  }
+	  if (row[1] != NULL) {
+	    print_buffer("Key: ", (uint8_t*)row[1], KEY_LENGTH);
+        memcpy(k, row[1], KEY_LENGTH);
+      }
+	  //if (row[3] != NULL)
+	  {
+	    print_buffer("OPc: ", (uint8_t*)row[2], KEY_LENGTH);
+	  //} else {
+	    ComputeOPc( k, opP, opc);
+	    update_length = sprintf(update, "UPDATE `users` SET `OPc`=UNHEX('");
+	    for (i = 0; i < KEY_LENGTH; i ++) {
+	    	update_length += sprintf(&update[update_length], "%02x", opc[i]);
+	    }
+	    update_length += sprintf(&update[update_length], "') WHERE `users`.`imsi`='%s'", (uint8_t*)row[0]);
+	    DB_DEBUG("Query: %s\n", update);
+
+	    if (mysql_query(db_desc->db_conn, update)) {
+	      DB_ERROR("Query execution failed: %s\n",
+	               mysql_error(db_desc->db_conn));
+	    } else {
+	        printf("IMSI %s Updated OPc ", (uint8_t*)row[0]);
+		    for (i = 0; i < KEY_LENGTH; i ++) {
+		    	printf("%02x", (uint8_t)(row[2][i]));
+		    }
+	        printf(" -> ");
+		    for (i = 0; i < KEY_LENGTH; i ++) {
+		    	printf("%02x", opc[i]);
+		    }
+	        printf("\n");
+
+	      /* process each statement result */
+	      do {
+	        /* did current statement return data? */
+	        res2 = mysql_store_result(db_desc->db_conn);
+
+	        if (res2) {
+	          /* yes; process rows and free the result set */
+	          mysql_free_result(res2);
+	        } else {      /* no result set or error */
+	          if (mysql_field_count(db_desc->db_conn) == 0) {
+	            DB_ERROR("%lld rows affected\n",
+	                   mysql_affected_rows(db_desc->db_conn));
+	          } else { /* some error occurred */
+	            DB_ERROR("Could not retrieve result set\n");
+	            break;
+	          }
+	        }
+
+	        /* more results? -1 = no, >0 = error, 0 = yes (keep looping) */
+	        if ((status = mysql_next_result(db_desc->db_conn)) > 0)
+	          DB_ERROR("Could not execute statement\n");
+	      } while (status == 0);
+	    }
+	  }
+    }
+  }
+
+  mysql_free_result(res);
+  mysql_thread_end();
+
+  return ret;
+
+}
+
+
diff --git a/openair-cn/OPENAIRHSS/db/db_proto.h b/openair-cn/OPENAIRHSS/db/db_proto.h
index 1e08236ec1..d87ed461e5 100644
--- a/openair-cn/OPENAIRHSS/db/db_proto.h
+++ b/openair-cn/OPENAIRHSS/db/db_proto.h
@@ -72,26 +72,33 @@ typedef struct {
 #define SQN_LENGTH  (6)
 #define RAND_LENGTH (16)
 
-typedef struct {
+typedef struct mysql_auth_info_resp_s{
   uint8_t key[KEY_LENGTH];
   uint8_t sqn[SQN_LENGTH];
   /* RAND should not be here... */
   uint8_t rand[RAND_LENGTH];
+  uint8_t opc[KEY_LENGTH];
 } mysql_auth_info_resp_t;
 
-typedef struct {
+typedef struct mysql_opc_push_s{
+  char imsi[IMSI_LENGTH_MAX + 1];
+  /* New computed SQN that will be used on next auth info req */
+  uint8_t sqn[SQN_LENGTH];
+} mysql_opc_push_t;
+
+typedef struct mysql_sqn_push_s{
   char imsi[IMSI_LENGTH_MAX + 1];
   /* New computed SQN that will be used on next auth info req */
   uint8_t sqn[SQN_LENGTH];
 } mysql_sqn_push_t;
 
-typedef struct {
+typedef struct mysql_mme_identity_s{
   /* An MME may have already been registered as serving the UE. */
   char mme_host[255];
   char mme_realm[200];
 } mysql_mme_identity_t;
 
-typedef struct {
+typedef struct mysql_ul_ans_s{
   char imsi[16];
   /* MSISDN this parameter may be NULL */
   char msisdn[16];
@@ -107,7 +114,7 @@ typedef struct {
   mysql_mme_identity_t mme_identity;
 } mysql_ul_ans_t;
 
-typedef struct {
+typedef struct mysql_ul_push_s{
   /* Bit masks indicating presence of optional fields */
 #define MME_IDENTITY_PRESENT           (0x1)
 #define MME_SUPPORTED_FEATURES_PRESENT (0x1)
@@ -140,12 +147,12 @@ typedef enum {
   IPV4_OR_IPV6 = 3,
 } pdn_type_t;
 
-typedef struct {
+typedef struct pdn_address_s{
   char ipv4_address[INET_ADDRSTRLEN];
   char ipv6_address[INET6_ADDRSTRLEN];
 } pdn_address_t;
 
-typedef struct {
+typedef struct mysql_pdn_s{
   char          apn[61];
   pdn_type_t    pdn_type;
   pdn_address_t pdn_address;
@@ -157,7 +164,7 @@ typedef struct {
   pre_emp_vul_t pre_emp_vul;
 } mysql_pdn_t;
 
-typedef struct {
+typedef struct mysql_pu_req_s{
   /* IMSI */
   char imsi[16];
 } mysql_pu_req_t;
@@ -193,4 +200,7 @@ int hss_mysql_push_rand_sqn(const char *imsi, uint8_t *rand_p, uint8_t *sqn);
 
 int hss_mysql_increment_sqn(const char *imsi);
 
+int hss_mysql_check_opc_keys(const uint8_t const opP[16]);
+
+
 #endif /* DB_PROTO_H_ */
diff --git a/openair-cn/OPENAIRHSS/db/oai_db.sql b/openair-cn/OPENAIRHSS/db/oai_db.sql
index 2f2a1ae2d3..c763ce4afb 100644
--- a/openair-cn/OPENAIRHSS/db/oai_db.sql
+++ b/openair-cn/OPENAIRHSS/db/oai_db.sql
@@ -3,7 +3,7 @@
 -- http://www.phpmyadmin.net
 --
 -- Host: localhost
--- Generation Time: May 04, 2015 at 10:41 AM
+-- Generation Time: May 28, 2015 at 02:32 PM
 -- Server version: 5.5.43-0ubuntu0.14.04.1
 -- PHP Version: 5.5.9-1ubuntu4.9
 
@@ -80,22 +80,19 @@ CREATE TABLE IF NOT EXISTS `pdn` (
   PRIMARY KEY (`id`,`pgw_id`,`users_imsi`),
   KEY `fk_pdn_pgw1_idx` (`pgw_id`),
   KEY `fk_pdn_users1_idx` (`users_imsi`)
-) ENGINE=MyISAM  DEFAULT CHARSET=latin1 AUTO_INCREMENT=19 ;
+) ENGINE=MyISAM  DEFAULT CHARSET=latin1 AUTO_INCREMENT=36 ;
 
 --
 -- Dumping data for table `pdn`
 --
 
 INSERT INTO `pdn` (`id`, `apn`, `pdn_type`, `pdn_ipv4`, `pdn_ipv6`, `aggregate_ambr_ul`, `aggregate_ambr_dl`, `pgw_id`, `users_imsi`, `qci`, `priority_level`, `pre_emp_cap`, `pre_emp_vul`, `LIPA-Permissions`) VALUES
-(18, 'oai.ipv4', 'IPv4', '0.0.0.0', '0:0:0:0:0:0:0:0', 50000000, 100000000, 3, '208930000000001', 9, 15, 'DISABLED', 'ENABLED', 'LIPA-only'),
-(1, 'oai.ipv4', 'IPv4', '0.0.0.0', '0:0:0:0:0:0:0:0', 50000000, 100000000, 3, '20834123456789', 9, 15, 'DISABLED', 'ENABLED', 'LIPA-only'),
-(8, 'oai.ipv4', 'IPv4', '0.0.0.0', '0:0:0:0:0:0:0:0', 50000000, 100000000, 3, '208920000000008', 9, 15, 'DISABLED', 'ENABLED', 'LIPA-only'),
-(9, 'oai.ipv4', 'IPv4', '0.0.0.0', '0:0:0:0:0:0:0:0', 50000000, 100000000, 3, '208920000000009', 9, 15, 'DISABLED', 'ENABLED', 'LIPA-only'),
+(1, 'oai.ipv4', 'IPv4', '0.0.0.0', '0:0:0:0:0:0:0:0', 50000000, 100000000, 3, '208930000000001', 9, 15, 'DISABLED', 'ENABLED', 'LIPA-only'),
+(11, 'oai.ipv4', 'IPv4', '0.0.0.0', '0:0:0:0:0:0:0:0', 50000000, 100000000, 3, '20834123456789', 9, 15, 'DISABLED', 'ENABLED', 'LIPA-only'),
 (10, 'oai.ipv4', 'IPv4', '0.0.0.0', '0:0:0:0:0:0:0:0', 50000000, 100000000, 3, '20810000001234', 9, 15, 'DISABLED', 'ENABLED', 'LIPA-only'),
-(11, 'oai.ipv4', 'IPv4', '0.0.0.0', '0:0:0:0:0:0:0:0', 50000000, 100000000, 3, '208920000000053', 9, 15, 'DISABLED', 'ENABLED', 'LIPA-only'),
-(12, 'oai.ipv4', 'IPv4', '0.0.0.0', '0:0:0:0:0:0:0:0', 50000000, 100000000, 3, '208920000000055', 9, 15, 'DISABLED', 'ENABLED', 'LIPA-only'),
-(13, 'oai.ipv4', 'IPv4', '0.0.0.0', '0:0:0:0:0:0:0:0', 50000000, 100000000, 3, '31002890832150', 9, 15, 'DISABLED', 'ENABLED', 'LIPA-only'),
-(16, 'oai.ipv4', 'IPv4', '0.0.0.0', '0:0:0:0:0:0:0:0', 50000000, 100000000, 3, '208920000000054', 9, 15, 'DISABLED', 'ENABLED', 'LIPA-only');
+(12, 'oai.ipv4', 'IPv4', '0.0.0.0', '0:0:0:0:0:0:0:0', 50000000, 100000000, 3, '31002890832150', 9, 15, 'DISABLED', 'ENABLED', 'LIPA-only'),
+(2, 'oai.ipv4', 'IPv4', '0.0.0.0', '0:0:0:0:0:0:0:0', 50000000, 100000000, 3, '208950000000002', 9, 15, 'DISABLED', 'ENABLED', 'LIPA-only'),
+(3, 'oai.ipv4', 'IPv4', '0.0.0.0', '0:0:0:0:0:0:0:0', 50000000, 100000000, 3, '001010123456789', 9, 15, 'DISABLED', 'ENABLED', 'LIPA-only');
 
 -- --------------------------------------------------------
 
@@ -156,6 +153,7 @@ CREATE TABLE IF NOT EXISTS `users` (
   `urrp_mme` tinyint(1) NOT NULL DEFAULT '0' COMMENT 'UE Reachability Request Parameter indicating that UE activity notification from MME has been requested by the HSS.',
   `sqn` bigint(20) unsigned zerofill NOT NULL,
   `rand` varbinary(16) NOT NULL,
+  `OPc` varbinary(16) DEFAULT NULL COMMENT 'Can be computed by HSS',
   PRIMARY KEY (`imsi`,`mmeidentity_idmmeidentity`),
   KEY `fk_users_mmeidentity_idx1` (`mmeidentity_idmmeidentity`)
 ) ENGINE=MyISAM DEFAULT CHARSET=latin1;
@@ -164,17 +162,13 @@ CREATE TABLE IF NOT EXISTS `users` (
 -- Dumping data for table `users`
 --
 
-INSERT INTO `users` (`imsi`, `msisdn`, `imei`, `imei_sv`, `ms_ps_status`, `rau_tau_timer`, `ue_ambr_ul`, `ue_ambr_dl`, `access_restriction`, `mme_cap`, `mmeidentity_idmmeidentity`, `key`, `RFSP-Index`, `urrp_mme`, `sqn`, `rand`) VALUES
-('20834123456789', '380561234567', '12345678', '23', 'PURGED', 50, 40000000, 100000000, 47, 0000000000, 2, '+�E��ų\0�,IH��H', 0, 0, 00000000000000000096, 'Px�X \Z1��x��'),
-('208920000000008', '33638060008', NULL, NULL, 'PURGED', 120, 40000000, 100000000, 47, 0000000000, 2, '��G?/�����	|hb', 1, 0, 00000000004294969388, '��I�j���>O�O�K)'),
-('208920000000009', '33638060009', NULL, NULL, 'PURGED', 120, 40000000, 100000000, 47, 0000000000, 2, '��G?/�ДWdiHC׾�', 1, 0, 00000000000000033361, '\ZM{�h��#�\\*l��'),
-('20810000001234', '33611123456', NULL, NULL, 'PURGED', 120, 40000000, 100000000, 47, 0000000000, 2, '��G?/�����	|hb', 1, 0, 00000281454575616097, 's���$r�C�f�=:�x�'),
-('208920000000053', '33638060053', '35611302209415', NULL, 'PURGED', 120, 40000000, 100000000, 47, 0000000000, 2, '��G?/�Д6�`FB&w', 1, 0, 00000000004294972622, 'o.�q@�#�\0�^���4�'),
-('208920000000055', '33638060055', NULL, NULL, 'PURGED', 120, 40000000, 100000000, 47, 0000000000, 2, '��G?/�ДM_^r	v', 1, 0, 00000000004294969388, '��I�j���>O�O�K)'),
-('31002890832150', '33638060059', '35611302209414', NULL, 'PURGED', 120, 40000000, 100000000, 47, 0000000000, 2, '��G?/�Д����	|hb', 1, 0, 00000000000000012416, '`�F�݆��D��ϛ���'),
-('208920000000054', '33638060054', NULL, NULL, 'PURGED', 120, 40000000, 100000000, 47, 0000000000, 2, '��G?/�ДM_^r	v', 1, 0, 00000000000000039820, 'B@W�شJUٰTo�C'),
-('001010123456789', '33600101789', NULL, NULL, 'PURGED', 120, 50000000, 100000000, 47, 0000000000, 2, '\0	\n\r', 1, 0, 00000000000000000032, '��e�E�S��Aj��|'),
-('208930000000001', '33638060008', NULL, NULL, 'NOT_PURGED', 120, 50000000, 100000000, 47, 0000000000, 2, '��G?/�����	|hb', 1, 0, 00000000000000002487, '�$�N!�Mp�-T�)#�');
+INSERT INTO `users` (`imsi`, `msisdn`, `imei`, `imei_sv`, `ms_ps_status`, `rau_tau_timer`, `ue_ambr_ul`, `ue_ambr_dl`, `access_restriction`, `mme_cap`, `mmeidentity_idmmeidentity`, `key`, `RFSP-Index`, `urrp_mme`, `sqn`, `rand`, `OPc`) VALUES
+('20834123456789', '380561234567', '12345678', '23', 'PURGED', 50, 40000000, 100000000, 47, 0000000000, 2, '+�E��ų\0�,IH��H', 0, 0, 00000000000000000096, 'Px�X \Z1��x��', 'g퐐jS+Aq���6Y'),
+('20810000001234', '33611123456', NULL, NULL, 'PURGED', 120, 40000000, 100000000, 47, 0000000000, 2, '��G?/�����	|hb', 1, 0, 00000281454575616097, 's���$r�C�f�=:�x�', '�''��i.u2fz;`]'),
+('31002890832150', '33638060059', '35611302209414', NULL, 'PURGED', 120, 40000000, 100000000, 47, 0000000000, 2, '��G?/�Д����	|hb', 1, 0, 00000000000000012416, '`�F�݆��D��ϛ���', '�''��i.u2fz;`]'),
+('001010123456789', '33600101789', NULL, NULL, 'PURGED', 120, 50000000, 100000000, 47, 0000000000, 2, '\0	\n\r', 1, 0, 00000000000000000351, '\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0', '$�_|/+6��%/%���'),
+('208930000000001', '33638060008', NULL, NULL, 'PURGED', 120, 50000000, 100000000, 47, 0000000000, 2, '��G?/�Д����	|hb', 1, 0, 00000000000000006103, '��wq��gzW�Ё��Z]', '�''��i.u2fz;`]'),
+('208950000000002', '33638060009', NULL, NULL, 'PURGED', 120, 50000000, 100000000, 47, 0000000000, 2, '��G?/�����	|hb', 1, 0, 00000000000000006391, 'U�|� w�D��\Z�7', '�''��i.u2fz;`]');
 
 /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
 /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
diff --git a/openair-cn/OPENAIRHSS/db/pft_db.sql b/openair-cn/OPENAIRHSS/db/pft_db.sql
new file mode 100644
index 0000000000..e2eb8bfaa6
--- /dev/null
+++ b/openair-cn/OPENAIRHSS/db/pft_db.sql
@@ -0,0 +1,173 @@
+-- phpMyAdmin SQL Dump
+-- version 4.0.10deb1
+-- http://www.phpmyadmin.net
+--
+-- Host: localhost
+-- Generation Time: May 28, 2015 at 02:24 PM
+-- Server version: 5.5.43-0ubuntu0.14.04.1
+-- PHP Version: 5.5.9-1ubuntu4.9
+
+SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
+SET time_zone = "+00:00";
+
+
+/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
+/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
+/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
+/*!40101 SET NAMES utf8 */;
+
+--
+-- Database: `oai_db`
+--
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table `apn`
+--
+
+CREATE TABLE IF NOT EXISTS `apn` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `apn-name` varchar(60) NOT NULL,
+  `pdn-type` enum('IPv4','IPv6','IPv4v6','IPv4_or_IPv6') NOT NULL,
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `apn-name` (`apn-name`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table `mmeidentity`
+--
+
+CREATE TABLE IF NOT EXISTS `mmeidentity` (
+  `idmmeidentity` int(11) NOT NULL AUTO_INCREMENT,
+  `mmehost` varchar(255) DEFAULT NULL,
+  `mmerealm` varchar(200) DEFAULT NULL,
+  `UE-Reachability` tinyint(1) NOT NULL COMMENT 'Indicates whether the MME supports UE Reachability Notifcation',
+  PRIMARY KEY (`idmmeidentity`)
+) ENGINE=MyISAM  DEFAULT CHARSET=latin1 AUTO_INCREMENT=43 ;
+
+--
+-- Dumping data for table `mmeidentity`
+--
+
+INSERT INTO `mmeidentity` (`idmmeidentity`, `mmehost`, `mmerealm`, `UE-Reachability`) VALUES
+(2, 'yang.openair4G.eur', 'openair4G.eur', 0),
+(1, 'ng40-erc.openair4G.eur', 'openair4G.eur', 0);
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table `pdn`
+--
+
+CREATE TABLE IF NOT EXISTS `pdn` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `apn` varchar(60) NOT NULL,
+  `pdn_type` enum('IPv4','IPv6','IPv4v6','IPv4_or_IPv6') NOT NULL DEFAULT 'IPv4',
+  `pdn_ipv4` varchar(15) DEFAULT '0.0.0.0',
+  `pdn_ipv6` varchar(45) CHARACTER SET latin1 COLLATE latin1_general_ci DEFAULT '0:0:0:0:0:0:0:0',
+  `aggregate_ambr_ul` int(10) unsigned DEFAULT '50000000',
+  `aggregate_ambr_dl` int(10) unsigned DEFAULT '100000000',
+  `pgw_id` int(11) NOT NULL,
+  `users_imsi` varchar(15) NOT NULL,
+  `qci` tinyint(3) unsigned NOT NULL DEFAULT '9',
+  `priority_level` tinyint(3) unsigned NOT NULL DEFAULT '15',
+  `pre_emp_cap` enum('ENABLED','DISABLED') DEFAULT 'DISABLED',
+  `pre_emp_vul` enum('ENABLED','DISABLED') DEFAULT 'DISABLED',
+  `LIPA-Permissions` enum('LIPA-prohibited','LIPA-only','LIPA-conditional') NOT NULL DEFAULT 'LIPA-only',
+  PRIMARY KEY (`id`,`pgw_id`,`users_imsi`),
+  KEY `fk_pdn_pgw1_idx` (`pgw_id`),
+  KEY `fk_pdn_users1_idx` (`users_imsi`)
+) ENGINE=MyISAM  DEFAULT CHARSET=latin1 AUTO_INCREMENT=36 ;
+
+--
+-- Dumping data for table `pdn`
+--
+
+INSERT INTO `pdn` (`id`, `apn`, `pdn_type`, `pdn_ipv4`, `pdn_ipv6`, `aggregate_ambr_ul`, `aggregate_ambr_dl`, `pgw_id`, `users_imsi`, `qci`, `priority_level`, `pre_emp_cap`, `pre_emp_vul`, `LIPA-Permissions`) VALUES
+(8, 'oai.ipv4', 'IPv4', '0.0.0.0', '0:0:0:0:0:0:0:0', 50000000, 100000000, 3, '208920000000008', 9, 15, 'DISABLED', 'ENABLED', 'LIPA-only'),
+(9, 'oai.ipv4', 'IPv4', '0.0.0.0', '0:0:0:0:0:0:0:0', 50000000, 100000000, 3, '208920000000009', 9, 15, 'DISABLED', 'ENABLED', 'LIPA-only'),
+(11, 'oai.ipv4', 'IPv4', '0.0.0.0', '0:0:0:0:0:0:0:0', 50000000, 100000000, 3, '208920000000053', 9, 15, 'DISABLED', 'ENABLED', 'LIPA-only'),
+(12, 'oai.ipv4', 'IPv4', '0.0.0.0', '0:0:0:0:0:0:0:0', 50000000, 100000000, 3, '208920000000055', 9, 15, 'DISABLED', 'ENABLED', 'LIPA-only'),
+(16, 'oai.ipv4', 'IPv4', '0.0.0.0', '0:0:0:0:0:0:0:0', 50000000, 100000000, 3, '208920000000054', 9, 15, 'DISABLED', 'ENABLED', 'LIPA-only');
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table `pgw`
+--
+
+CREATE TABLE IF NOT EXISTS `pgw` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `ipv4` varchar(15) NOT NULL,
+  `ipv6` varchar(39) NOT NULL,
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `ipv4` (`ipv4`),
+  UNIQUE KEY `ipv6` (`ipv6`)
+) ENGINE=MyISAM  DEFAULT CHARSET=latin1 AUTO_INCREMENT=4 ;
+
+--
+-- Dumping data for table `pgw`
+--
+
+INSERT INTO `pgw` (`id`, `ipv4`, `ipv6`) VALUES
+(1, '127.0.0.1', '0:0:0:0:0:0:0:1'),
+(2, '192.168.56.101', ''),
+(3, '10.0.0.2', '0');
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table `terminal-info`
+--
+
+CREATE TABLE IF NOT EXISTS `terminal-info` (
+  `imei` varchar(15) NOT NULL,
+  `sv` varchar(2) NOT NULL,
+  UNIQUE KEY `imei` (`imei`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table `users`
+--
+
+CREATE TABLE IF NOT EXISTS `users` (
+  `imsi` varchar(15) NOT NULL COMMENT 'IMSI is the main reference key.',
+  `msisdn` varchar(46) DEFAULT NULL COMMENT 'The basic MSISDN of the UE (Presence of MSISDN is optional).',
+  `imei` varchar(15) DEFAULT NULL COMMENT 'International Mobile Equipment Identity',
+  `imei_sv` varchar(2) DEFAULT NULL COMMENT 'International Mobile Equipment Identity Software Version Number',
+  `ms_ps_status` enum('PURGED','NOT_PURGED') DEFAULT 'PURGED' COMMENT 'Indicates that ESM and EMM status are purged from MME',
+  `rau_tau_timer` int(10) unsigned DEFAULT '120',
+  `ue_ambr_ul` bigint(20) unsigned DEFAULT '50000000' COMMENT 'The Maximum Aggregated uplink MBRs to be shared across all Non-GBR bearers according to the subscription of the user.',
+  `ue_ambr_dl` bigint(20) unsigned DEFAULT '100000000' COMMENT 'The Maximum Aggregated downlink MBRs to be shared across all Non-GBR bearers according to the subscription of the user.',
+  `access_restriction` int(10) unsigned DEFAULT '60' COMMENT 'Indicates the access restriction subscription information. 3GPP TS.29272 #7.3.31',
+  `mme_cap` int(10) unsigned zerofill DEFAULT NULL COMMENT 'Indicates the capabilities of the MME with respect to core functionality e.g. regional access restrictions.',
+  `mmeidentity_idmmeidentity` int(11) NOT NULL DEFAULT '0',
+  `key` varbinary(16) NOT NULL DEFAULT '0' COMMENT 'UE security key',
+  `RFSP-Index` smallint(5) unsigned NOT NULL DEFAULT '1' COMMENT 'An index to specific RRM configuration in the E-UTRAN. Possible values from 1 to 256',
+  `urrp_mme` tinyint(1) NOT NULL DEFAULT '0' COMMENT 'UE Reachability Request Parameter indicating that UE activity notification from MME has been requested by the HSS.',
+  `sqn` bigint(20) unsigned zerofill NOT NULL,
+  `rand` varbinary(16) NOT NULL,
+  `OPc` varbinary(16) DEFAULT NULL COMMENT 'Can be computed by HSS',
+  PRIMARY KEY (`imsi`,`mmeidentity_idmmeidentity`),
+  KEY `fk_users_mmeidentity_idx1` (`mmeidentity_idmmeidentity`)
+) ENGINE=MyISAM DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `users`
+--
+
+INSERT INTO `users` (`imsi`, `msisdn`, `imei`, `imei_sv`, `ms_ps_status`, `rau_tau_timer`, `ue_ambr_ul`, `ue_ambr_dl`, `access_restriction`, `mme_cap`, `mmeidentity_idmmeidentity`, `key`, `RFSP-Index`, `urrp_mme`, `sqn`, `rand`, `OPc`) VALUES
+('208920000000008', '33638060008', NULL, NULL, 'PURGED', 120, 40000000, 100000000, 47, 0000000000, 2, '��G?/�����	|hb', 1, 0, 00000000004294969388, '��I�j���>O�O�K)', '�''��i.u2fz;`]'),
+('208920000000009', '33638060009', NULL, NULL, 'PURGED', 120, 40000000, 100000000, 47, 0000000000, 2, '��G?/�ДWdiHC׾�', 1, 0, 00000000000000033361, '\ZM{�h��#�\\*l��', '����x�J��	��'),
+('208920000000053', '33638060053', '35611302209415', NULL, 'PURGED', 120, 40000000, 100000000, 47, 0000000000, 2, '��G?/�Д6�`FB&w', 1, 0, 00000000004294972622, 'o.�q@�#�\0�^���4�', 'O��~�����ɭ��'),
+('208920000000055', '33638060055', NULL, NULL, 'PURGED', 120, 40000000, 100000000, 47, 0000000000, 2, '��G?/�ДM_^r	v', 1, 0, 00000000004294969388, '��I�j���>O�O�K)', 'N�0;�bCR����_'),
+('208920000000054', '33638060054', NULL, NULL, 'PURGED', 120, 40000000, 100000000, 47, 0000000000, 2, '��G?/�ДM_^r	v', 1, 0, 00000000000000039820, 'B@W�شJUٰTo�C', 'N�0;�bCR����_');
+
+/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
+/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
+/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
diff --git a/openair-cn/OPENAIRHSS/hss_main.c b/openair-cn/OPENAIRHSS/hss_main.c
index be21f1c1f6..5a226bc0d9 100644
--- a/openair-cn/OPENAIRHSS/hss_main.c
+++ b/openair-cn/OPENAIRHSS/hss_main.c
@@ -37,6 +37,7 @@
 
 hss_config_t hss_config;
 
+
 int main(int argc, char *argv[])
 {
 
@@ -52,6 +53,10 @@ int main(int argc, char *argv[])
 
   random_init();
 
+  if (hss_config.valid_op) {
+    hss_mysql_check_opc_keys((uint8_t*)hss_config.operator_key_bin);
+  }
+
   s6a_init(&hss_config);
 
   while(1) {
diff --git a/openair-cn/OPENAIRHSS/s6a/s6a_auth_info.c b/openair-cn/OPENAIRHSS/s6a/s6a_auth_info.c
index 69651c9116..718e48cce4 100644
--- a/openair-cn/OPENAIRHSS/s6a/s6a_auth_info.c
+++ b/openair-cn/OPENAIRHSS/s6a/s6a_auth_info.c
@@ -210,7 +210,7 @@ int s6a_auth_info_cb(struct msg **msg, struct avp *paramavp,
 
   if (auts != NULL) {
     /* Try to derive SQN_MS from previous RAND */
-    sqn = sqn_ms_derive(auth_info_resp.key, auts, auth_info_resp.rand);
+    sqn = sqn_ms_derive(auth_info_resp.opc, auth_info_resp.key, auts, auth_info_resp.rand);
 
     if (sqn != NULL) {
       /* We succeeded to verify SQN_MS... */
@@ -245,7 +245,7 @@ int s6a_auth_info_cb(struct msg **msg, struct avp *paramavp,
   hss_mysql_increment_sqn(auth_info_req.imsi);
 
   /* Generate authentication vector */
-  generate_vector(imsi, auth_info_resp.key,
+  generate_vector(auth_info_resp.opc, imsi, auth_info_resp.key,
                   hdr->avp_value->os.data, sqn, &vector);
 
   /* We add the vector */
diff --git a/openair-cn/OPENAIRHSS/utils/hss_config.c b/openair-cn/OPENAIRHSS/utils/hss_config.c
index 6428d11f80..415504fc75 100644
--- a/openair-cn/OPENAIRHSS/utils/hss_config.c
+++ b/openair-cn/OPENAIRHSS/utils/hss_config.c
@@ -65,8 +65,6 @@ int fd_g_debug_lvl = 1;
 
 /* YACC forward declarations */
 extern int  yyparse (struct hss_config_s *hss_config_p);
-extern uint8_t opc[16];
-extern uint8_t op [16];
 static int config_parse_command_line(int argc, char *argv[],
                                      hss_config_t *hss_config_p);
 static int config_parse_file(hss_config_t *hss_config_p);
@@ -91,7 +89,6 @@ int config_init(int argc, char *argv[], hss_config_t *hss_config_p)
   }
 
   hss_config_p->valid_op = 0;
-  hss_config_p->valid_opc = 0;
 
   if ((ret = config_parse_command_line(argc, argv, hss_config_p)) != 0) {
     return ret;
@@ -103,22 +100,38 @@ int config_init(int argc, char *argv[], hss_config_t *hss_config_p)
     /* Parsing of the file failed. -> abort */
     abort();
   }
-
-  config_display(hss_config_p);
+  if (hss_config_p->random) {
+    if (strcasecmp(hss_config_p->random, "false") == 0) {
+      hss_config_p->random_bool = 0;
+    } else if (strcasecmp(hss_config_p->random, "true") == 0) {
+      hss_config_p->random_bool = 1;
+    } else {
+      fprintf(stderr,
+                "Error in configuration file: random: %s (allowed values {true,false})\n",
+                hss_config_p->random);
+        abort();
+    }
+  } else {
+	  hss_config_p->random      = "true";
+	  hss_config_p->random_bool = 1;
+      fprintf(stderr,
+                "Default values for random: %s (allowed values {true,false})\n",
+                hss_config_p->random);
+  }
 
   // post processing for op key
   if (hss_config_p->operator_key) {
     if (strlen(hss_config_p->operator_key) == 32) {
       ret = sscanf(hss_config_p->operator_key,
                    "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
-                   (unsigned int*)&op[0],(unsigned int*)&op[1],
-                   (unsigned int*)&op[2],(unsigned int*)&op[3],
-                   (unsigned int*)&op[4],(unsigned int*)&op[5],
-                   (unsigned int*)&op[6],(unsigned int*)&op[7],
-                   (unsigned int*)&op[8],(unsigned int*)&op[9],
-                   (unsigned int*)&op[10],(unsigned int*)&op[11],
-                   (unsigned int*)&op[12],(unsigned int*)&op[13],
-                   (unsigned int*)&op[14],(unsigned int*)&op[15]);
+                   (unsigned int*)&hss_config_p->operator_key_bin[0],(unsigned int*)&hss_config_p->operator_key_bin[1],
+                   (unsigned int*)&hss_config_p->operator_key_bin[2],(unsigned int*)&hss_config_p->operator_key_bin[3],
+                   (unsigned int*)&hss_config_p->operator_key_bin[4],(unsigned int*)&hss_config_p->operator_key_bin[5],
+                   (unsigned int*)&hss_config_p->operator_key_bin[6],(unsigned int*)&hss_config_p->operator_key_bin[7],
+                   (unsigned int*)&hss_config_p->operator_key_bin[8],(unsigned int*)&hss_config_p->operator_key_bin[9],
+                   (unsigned int*)&hss_config_p->operator_key_bin[10],(unsigned int*)&hss_config_p->operator_key_bin[11],
+                   (unsigned int*)&hss_config_p->operator_key_bin[12],(unsigned int*)&hss_config_p->operator_key_bin[13],
+                   (unsigned int*)&hss_config_p->operator_key_bin[14],(unsigned int*)&hss_config_p->operator_key_bin[15]);
 
       if (ret != 16) {
         fprintf(stderr,
@@ -129,32 +142,9 @@ int config_init(int argc, char *argv[], hss_config_t *hss_config_p)
       hss_config_p->valid_op = 1;
     }
   }
-  if (hss_config_p->operator_ckey) {
-    if (strlen(hss_config_p->operator_ckey) == 32) {
-      ret = sscanf(hss_config_p->operator_ckey,
-                   "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
-                   (unsigned int*)&opc[0],(unsigned int*)&opc[1],
-                   (unsigned int*)&opc[2],(unsigned int*)&opc[3],
-                   (unsigned int*)&opc[4],(unsigned int*)&opc[5],
-                   (unsigned int*)&opc[6],(unsigned int*)&opc[7],
-                   (unsigned int*)&opc[8],(unsigned int*)&opc[9],
-                   (unsigned int*)&opc[10],(unsigned int*)&opc[11],
-                   (unsigned int*)&opc[12],(unsigned int*)&opc[13],
-                   (unsigned int*)&opc[14],(unsigned int*)&opc[15]);
+  config_display(hss_config_p);
+
 
-      if (ret != 16) {
-        fprintf(stderr,
-                "Error in configuration file: operator ckey: %s\n",
-                hss_config_p->operator_ckey);
-        abort();
-      }
-      hss_config_p->valid_opc = 1;
-    }
-  }
-  if ((hss_config_p->valid_opc == 0) && (hss_config_p->valid_op ==0)) {
-    fprintf(stderr, "Error in configuration file: no valid OP or OPC key\n");
-    abort();
-  }
   return 0;
 }
 
@@ -195,8 +185,8 @@ static void config_display(hss_config_t *hss_config_p)
   fprintf(stdout, "* Security:\n");
   fprintf(stdout, "\t- Operator key......: %s\n",
           hss_config_p->operator_key);
-  fprintf(stdout, "\t- Operator ckey......: %s\n",
-          hss_config_p->operator_ckey);
+  fprintf(stdout, "\t- Random      ......: %s\n",
+		  hss_config_p->random);
 }
 
 static int config_parse_command_line(int argc, char *argv[],
diff --git a/openair-cn/OPENAIRHSS/utils/hss_config.h b/openair-cn/OPENAIRHSS/utils/hss_config.h
index a3bd22979b..6f3425c6d0 100644
--- a/openair-cn/OPENAIRHSS/utils/hss_config.h
+++ b/openair-cn/OPENAIRHSS/utils/hss_config.h
@@ -37,15 +37,17 @@ typedef struct hss_config_s {
 
 
   char *operator_key;
+  unsigned char operator_key_bin[16];
   int   valid_op;
-  char *operator_ckey;
-  int   valid_opc;
 
   /* The freediameter configuration file */
   char *freediameter_config;
 
   /* THe HSS global configuration file */
   char *config;
+
+  char *random;
+  char  random_bool;
 } hss_config_t;
 
 int config_init(int argc, char *argv[], hss_config_t *hss_config_p);
diff --git a/openair-cn/OPENAIRHSS/utils/hss_parser.y b/openair-cn/OPENAIRHSS/utils/hss_parser.y
index b01de3552d..5fb532dcdf 100644
--- a/openair-cn/OPENAIRHSS/utils/hss_parser.y
+++ b/openair-cn/OPENAIRHSS/utils/hss_parser.y
@@ -70,7 +70,7 @@ int fddlex(YYSTYPE *lvalp, YYLTYPE *llocp);
 %token MYSQL_PASS
 %token MYSQL_DB
 %token OPERATOR_KEY
-%token OPERATOR_CKEY
+%token RANDOM
 
 %%
 conffile:       /* Empty is OK -- for simplicity here, we reject in daemon later */
@@ -79,7 +79,7 @@ conffile:       /* Empty is OK -- for simplicity here, we reject in daemon later
     | conffile mysql_user
     | conffile mysql_pass
     | conffile operator_key
-    | conffile operator_ckey
+    | conffile random
     | conffile fdconf
     | conffile errors
     {
@@ -118,9 +118,9 @@ operator_key: OPERATOR_KEY '=' QSTRING ';'
     }
     ;
 
-operator_ckey: OPERATOR_CKEY '=' QSTRING ';'
+random: RANDOM '=' QSTRING ';'
     {
-        hss_config_p->operator_ckey = $3;
+        hss_config_p->random = $3;
     }
     ;
 
diff --git a/openair-cn/OPENAIRHSS/utils/hss_scanner.l b/openair-cn/OPENAIRHSS/utils/hss_scanner.l
index 9c3590ae7e..e9c43c9a2b 100644
--- a/openair-cn/OPENAIRHSS/utils/hss_scanner.l
+++ b/openair-cn/OPENAIRHSS/utils/hss_scanner.l
@@ -114,7 +114,7 @@ qstring     \"[^\"\n]*\"
 (?i:"MYSQL_pass")   { return MYSQL_PASS; }
 (?i:"MYSQL_db")     { return MYSQL_DB; }
 (?i:"OPERATOR_key")     { return OPERATOR_KEY; }
-(?i:"OPERATOR_ckey")     { return OPERATOR_CKEY; }
+(?i:"RANDOM")       { return RANDOM; }
 
     /* Valid single characters for yyparse */
 <*>[=,:;{}]     { return yytext[0]; }
-- 
GitLab