Changing logic in GroupManger.getGroupKey() to avoid regenerate group key pairs every time.

Change-Id: I4c6eb5246bce04289d7ba097a66d0f11745ce44c
Refs: #3812
diff --git a/src/group-manager-db.cpp b/src/group-manager-db.cpp
index 5c9cd23..0f8640b 100644
--- a/src/group-manager-db.cpp
+++ b/src/group-manager-db.cpp
@@ -55,7 +55,17 @@
   "      ON UPDATE CASCADE                            \n"
   "  );                                               \n"
   "CREATE UNIQUE INDEX IF NOT EXISTS                  \n"
-  "   memNameIndex ON members(member_name);           \n";
+  "   memNameIndex ON members(member_name);           \n"
+  "                                                   \n"
+  "CREATE TABLE IF NOT EXISTS                         \n"
+  "  ekeys(                                           \n"
+  "    ekey_id             INTEGER PRIMARY KEY,       \n"
+  "    ekey_name           BLOB NOT NULL,             \n"
+  "    pub_key             BLOB NOT NULL,             \n"
+  "    pri_key             BLOB NOT NULL              \n"
+  "  );                                               \n"
+  "CREATE UNIQUE INDEX IF NOT EXISTS                  \n"
+  "   ekeyNameIndex ON ekeys(ekey_name);              \n";
 
 class GroupManagerDB::Impl
 {
@@ -319,5 +329,60 @@
   statement.step();
 }
 
+bool
+GroupManagerDB::hasEKey(const Name& eKeyName)
+{
+  Sqlite3Statement statement(m_impl->m_database,
+                             "SELECT ekey_id FROM ekeys where ekey_name=?");
+  statement.bind(1, eKeyName.wireEncode(), SQLITE_TRANSIENT);
+  return (statement.step() == SQLITE_ROW);
+}
+
+void
+GroupManagerDB::addEKey(const Name& eKeyName, const Buffer& pubKey, const Buffer& priKey)
+{
+  Sqlite3Statement statement(m_impl->m_database,
+                             "INSERT INTO ekeys(ekey_name, pub_key, pri_key) values (?, ?, ?)");
+  statement.bind(1, eKeyName.wireEncode(), SQLITE_TRANSIENT);
+  statement.bind(2, pubKey.buf(), pubKey.size(), SQLITE_TRANSIENT);
+  statement.bind(3, priKey.buf(), priKey.size(), SQLITE_TRANSIENT);
+  if (statement.step() != SQLITE_DONE)
+    BOOST_THROW_EXCEPTION(Error("Cannot add the EKey to database"));
+}
+
+std::tuple<Buffer, Buffer>
+GroupManagerDB::getEKey(const Name& eKeyName)
+{
+  Sqlite3Statement statement(m_impl->m_database,
+                             "SELECT * FROM ekeys where ekey_name=?");
+  statement.bind(1, eKeyName.wireEncode(), SQLITE_TRANSIENT);
+
+  Buffer pubKey, priKey;
+  if (statement.step() == SQLITE_ROW) {
+    pubKey = Buffer(statement.getBlob(2), statement.getSize(2));
+    priKey = Buffer(statement.getBlob(3), statement.getSize(3));
+  }
+  else {
+    BOOST_THROW_EXCEPTION(Error("Cannot get the result from database"));
+  }
+  return std::make_tuple(pubKey, priKey);
+}
+
+void
+GroupManagerDB::cleanEKeys()
+{
+  Sqlite3Statement statement(m_impl->m_database, "DELETE FROM ekeys");
+  statement.step();
+}
+
+void
+GroupManagerDB::deleteEKey(const Name& eKeyName)
+{
+  Sqlite3Statement statement(m_impl->m_database,
+                             "DELETE FROM ekeys WHERE ekey_name=?");
+  statement.bind(1, eKeyName.wireEncode(), SQLITE_TRANSIENT);
+  statement.step();
+}
+
 } // namespace gep
 } // namespace ndn
diff --git a/src/group-manager-db.hpp b/src/group-manager-db.hpp
index 9f737ae..cbf4ecd 100644
--- a/src/group-manager-db.hpp
+++ b/src/group-manager-db.hpp
@@ -164,6 +164,42 @@
   void
   deleteMember(const Name& identity);
 
+  /**
+   * @brief Check if there is a EKey with name @p eKeyName in database
+   */
+  bool
+  hasEKey(const Name& eKeyName);
+
+  /**
+   * @brief Add a EKey with name @p eKeyName to database
+   *
+   * @p pubKey The public Key of the group key pair
+   * @p priKey The private Key of the group key pair
+   */
+  void
+  addEKey(const Name& eKeyName, const Buffer& pubKey, const Buffer& priKey);
+
+  /**
+   * @brief Get the group key pair from database
+   */
+  std::tuple<Buffer, Buffer>
+  getEKey(const Name& eKeyName);
+
+  /**
+   * @brief Delete all the EKeys in the database
+   *
+   * The database will keep growing because EKeys will keep being added. The method
+   * should be called periodically
+   */
+  void
+  cleanEKeys();
+
+  /**
+   * @brief Delete a EKey with name @p eKeyName from database
+   */
+  void
+  deleteEKey(const Name& eKeyName);
+
 private:
   class Impl;
   unique_ptr<Impl> m_impl;
diff --git a/src/group-manager.cpp b/src/group-manager.cpp
index 4bbf19e..bd5feb0 100644
--- a/src/group-manager.cpp
+++ b/src/group-manager.cpp
@@ -39,7 +39,7 @@
 }
 
 std::list<Data>
-GroupManager::getGroupKey(const TimeStamp& timeslot)
+GroupManager::getGroupKey(const TimeStamp& timeslot, bool needRegenerate)
 {
   std::map<Name, Buffer> memberKeys;
   std::list<Data> result;
@@ -54,7 +54,19 @@
 
   // generate the pri key and pub key
   Buffer priKeyBuf, pubKeyBuf;
-  generateKeyPairs(priKeyBuf, pubKeyBuf);
+  Name eKeyName(m_namespace);
+  eKeyName.append(NAME_COMPONENT_E_KEY).append(startTs).append(endTs);
+
+  if (!needRegenerate && m_db.hasEKey(eKeyName)) {
+    std::tie(pubKeyBuf, priKeyBuf) = getEKey(eKeyName);
+  }
+  else {
+    generateKeyPairs(priKeyBuf, pubKeyBuf);
+    if (m_db.hasEKey(eKeyName)) {
+      deleteEKey(eKeyName);
+    }
+    addEKey(eKeyName, pubKeyBuf, priKeyBuf);
+  }
 
   // add the first element to the result
   // E-KEY (public key) data packet name convention:
@@ -200,5 +212,29 @@
   return data;
 }
 
+void
+GroupManager::addEKey(const Name& eKeyName, const Buffer& pubKey, const Buffer& priKey)
+{
+  m_db.addEKey(eKeyName, pubKey, priKey);
+}
+
+std::tuple<Buffer, Buffer>
+GroupManager::getEKey(const Name& eKeyName)
+{
+  return m_db.getEKey(eKeyName);
+}
+
+void
+GroupManager::deleteEKey(const Name& eKeyName)
+{
+  m_db.deleteEKey(eKeyName);
+}
+
+void
+GroupManager::cleanEKeys()
+{
+  m_db.cleanEKeys();
+}
+
 } // namespace ndn
 } // namespace ndn
diff --git a/src/group-manager.hpp b/src/group-manager.hpp
index e4674df..2e19ab1 100644
--- a/src/group-manager.hpp
+++ b/src/group-manager.hpp
@@ -62,14 +62,17 @@
    *
    * This method creates a group key if it does not
    * exist, and encrypts the key using public key of
-   * all eligible members
+   * all eligible members.
+   *
+   * @p needRegenerate should be true if 1.first time to call 2.a member was removed
+   *                   and it can be false if 1.not the first time to call 2.a member was added
    *
    * @returns The group key (the first one is the
    *          public key, and the rest are encrypted
    *          private key.
    */
   std::list<Data>
-  getGroupKey(const TimeStamp& timeslot);
+  getGroupKey(const TimeStamp& timeslot, bool needRegenerate = true);
 
   /// @brief Add @p schedule with @p scheduleName
   void
@@ -95,6 +98,7 @@
   void
   updateMemberSchedule(const Name& identity, const std::string& scheduleName);
 
+
 PUBLIC_WITH_TESTS_ELSE_PRIVATE:
   /**
    * @brief Calculate interval that covers @p timeslot
@@ -121,6 +125,22 @@
   createDKeyData(const std::string& startTs, const std::string& endTs, const Name& keyName,
                  const Buffer& priKeyBuf, const Buffer& certKey);
 
+  /// @brief Add a EKey to the database
+  void
+  addEKey(const Name& eKeyName, const Buffer& pubKey, const Buffer& priKey);
+
+  /// @brief Get the key pair from the database
+  std::tuple<Buffer, Buffer>
+  getEKey(const Name& eKeyName);
+
+  /// @brief Delete a EKey to the database
+  void
+  deleteEKey(const Name& eKeyName);
+
+  /// @brief The method should be called periodically because the table size will keep growing
+  void
+  cleanEKeys();
+
 private:
   Name m_namespace;
   GroupManagerDB m_db;