update

Change-Id: Ieca8d47963634a7bb410fc9b7c3860fd438a8fff
diff --git a/src/detail/ca-request-state.hpp b/src/detail/ca-request-state.hpp
index 120a283..fe4db23 100644
--- a/src/detail/ca-request-state.hpp
+++ b/src/detail/ca-request-state.hpp
@@ -111,12 +111,15 @@
   /**
    * @brief The encryption key for the requester.
    */
-  std::array<uint8_t, 16> encryptionKey;
+  std::array<uint8_t, 16> encryptionKey = {};
   /**
-   * @brief The AES block counter for the requester.
+   * @brief The last Initialization Vector used by the AES encryption.
    */
-  uint32_t aesBlockCounter = 0;
-
+  std::vector<uint8_t> encryptionIv;
+  /**
+   * @brief The last Initialization Vector used by the other side's AES encryption.
+   */
+  std::vector<uint8_t> decryptionIv;
   /**
    * @brief The challenge type.
    */
diff --git a/src/detail/ca-sqlite.cpp b/src/detail/ca-sqlite.cpp
index 05a4c80..199508f 100644
--- a/src/detail/ca-sqlite.cpp
+++ b/src/detail/ca-sqlite.cpp
@@ -67,7 +67,8 @@
     remaining_time INTEGER,
     challenge_secrets TEXT,
     encryption_key BLOB NOT NULL,
-    aes_block_counter INTEGER
+    last_iv BLOB,
+    expected_next_iv BLOB
   );
 CREATE UNIQUE INDEX IF NOT EXISTS
   RequestStateIdIndex ON RequestStates(request_id);
@@ -130,7 +131,7 @@
                              challenge_status, cert_request,
                              challenge_type, challenge_secrets,
                              challenge_tp, remaining_tries, remaining_time,
-                             request_type, encryption_key, aes_block_counter
+                             request_type, encryption_key, last_iv, expected_next_iv
                              FROM RequestStates where request_id = ?)_SQLTEXT_");
   statement.bind(1, requestId.data(), requestId.size(), SQLITE_TRANSIENT);
 
@@ -142,7 +143,8 @@
     state.challengeType = statement.getString(5);
     state.requestType = static_cast<RequestType>(statement.getInt(10));
     std::memcpy(state.encryptionKey.data(), statement.getBlob(11), statement.getSize(11));
-    state.aesBlockCounter = statement.getInt(12);
+    state.encryptionIv.assign(statement.getBlob(12), statement.getBlob(12) + statement.getSize(12));
+    state.decryptionIv.assign(statement.getBlob(13), statement.getBlob(13) + statement.getSize(13));
     if (state.challengeType != "") {
       ChallengeState challengeState(statement.getString(3), time::fromIsoString(statement.getString(7)),
                                     statement.getInt(8), time::seconds(statement.getInt(9)),
@@ -164,15 +166,16 @@
       m_database,
       R"_SQLTEXT_(INSERT OR ABORT INTO RequestStates (request_id, ca_name, status, request_type,
                   cert_request, challenge_type, challenge_status, challenge_secrets,
-                  challenge_tp, remaining_tries, remaining_time, encryption_key, aes_block_counter)
-                  values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?))_SQLTEXT_");
+                  challenge_tp, remaining_tries, remaining_time, encryption_key, last_iv, expected_next_iv)
+                  values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?))_SQLTEXT_");
   statement.bind(1, request.requestId.data(), request.requestId.size(), SQLITE_TRANSIENT);
   statement.bind(2, request.caPrefix.wireEncode(), SQLITE_TRANSIENT);
   statement.bind(3, static_cast<int>(request.status));
   statement.bind(4, static_cast<int>(request.requestType));
   statement.bind(5, request.cert.wireEncode(), SQLITE_TRANSIENT);
   statement.bind(12, request.encryptionKey.data(), request.encryptionKey.size(), SQLITE_TRANSIENT);
-  statement.bind(13, request.aesBlockCounter);
+  statement.bind(13, request.encryptionIv.data(), request.encryptionIv.size(), SQLITE_TRANSIENT);
+  statement.bind(14, request.decryptionIv.data(), request.decryptionIv.size(), SQLITE_TRANSIENT);
   if (request.challengeState) {
     statement.bind(6, request.challengeType, SQLITE_TRANSIENT);
     statement.bind(7, request.challengeState->challengeStatus, SQLITE_TRANSIENT);
@@ -193,7 +196,7 @@
   Sqlite3Statement statement(m_database,
                              R"_SQLTEXT_(UPDATE RequestStates
                              SET status = ?, challenge_type = ?, challenge_status = ?, challenge_secrets = ?,
-                             challenge_tp = ?, remaining_tries = ?, remaining_time = ?, aes_block_counter = ?
+                             challenge_tp = ?, remaining_tries = ?, remaining_time = ?, last_iv = ?, expected_next_iv = ?
                              WHERE request_id = ?)_SQLTEXT_");
   statement.bind(1, static_cast<int>(request.status));
   statement.bind(2, request.challengeType, SQLITE_TRANSIENT);
@@ -211,8 +214,9 @@
     statement.bind(6, 0);
     statement.bind(7, 0);
   }
-  statement.bind(8, request.aesBlockCounter);
-  statement.bind(9, request.requestId.data(), request.requestId.size(), SQLITE_TRANSIENT);
+  statement.bind(8, request.encryptionIv.data(), request.encryptionIv.size(), SQLITE_TRANSIENT);
+  statement.bind(9, request.decryptionIv.data(), request.decryptionIv.size(), SQLITE_TRANSIENT);
+  statement.bind(10, request.requestId.data(), request.requestId.size(), SQLITE_TRANSIENT);
 
   if (statement.step() != SQLITE_DONE) {
     addRequest(request);
@@ -226,7 +230,7 @@
   Sqlite3Statement statement(m_database, R"_SQLTEXT_(SELECT id, request_id, ca_name, status,
                              challenge_status, cert_request, challenge_type, challenge_secrets,
                              challenge_tp, remaining_tries, remaining_time, request_type,
-                             encryption_key, aes_block_counter
+                             encryption_key, last_iv, expected_next_iv
                              FROM RequestStates)_SQLTEXT_");
   while (statement.step() == SQLITE_ROW) {
     RequestState state;
@@ -237,7 +241,8 @@
     state.cert = security::Certificate(statement.getBlock(5));
     state.requestType = static_cast<RequestType>(statement.getInt(11));
     std::memcpy(state.encryptionKey.data(), statement.getBlob(12), statement.getSize(12));
-    state.aesBlockCounter = statement.getInt(13);
+    state.encryptionIv.assign(statement.getBlob(13), statement.getBlob(13) + statement.getSize(13));
+    state.decryptionIv.assign(statement.getBlob(14), statement.getBlob(14) + statement.getSize(14));
     if (state.challengeType != "") {
       ChallengeState challengeState(statement.getString(4), time::fromIsoString(statement.getString(8)),
                                     statement.getInt(9), time::seconds(statement.getInt(10)),
@@ -270,7 +275,8 @@
     state.cert = security::Certificate(statement.getBlock(5));
     state.requestType = static_cast<RequestType>(statement.getInt(11));
     std::memcpy(state.encryptionKey.data(), statement.getBlob(12), statement.getSize(12));
-    state.aesBlockCounter = statement.getInt(13);
+    state.encryptionIv.assign(statement.getBlob(13), statement.getBlob(13) + statement.getSize(13));
+    state.decryptionIv.assign(statement.getBlob(14), statement.getBlob(14) + statement.getSize(14));
     if (state.challengeType != "") {
       ChallengeState challengeState(statement.getString(4), time::fromIsoString(statement.getString(8)),
                                     statement.getInt(9), time::seconds(statement.getInt(10)),
diff --git a/src/detail/challenge-encoder.cpp b/src/detail/challenge-encoder.cpp
index 9ab567a..de7e3cc 100644
--- a/src/detail/challenge-encoder.cpp
+++ b/src/detail/challenge-encoder.cpp
@@ -30,18 +30,14 @@
   response.push_back(makeNonNegativeIntegerBlock(tlv::Status, static_cast<uint64_t>(request.status)));
   if (request.challengeState) {
     response.push_back(makeStringBlock(tlv::ChallengeStatus, request.challengeState->challengeStatus));
-    response.push_back(
-        makeNonNegativeIntegerBlock(tlv::RemainingTries, request.challengeState->remainingTries));
-    response.push_back(
-        makeNonNegativeIntegerBlock(tlv::RemainingTime, request.challengeState->remainingTime.count()));
+    response.push_back(makeNonNegativeIntegerBlock(tlv::RemainingTries,
+                                                   request.challengeState->remainingTries));
+    response.push_back(makeNonNegativeIntegerBlock(tlv::RemainingTime,
+                                                   request.challengeState->remainingTime.count()));
     if (request.challengeState->challengeStatus == "need-proof") {
-      response.push_back(
-              makeStringBlock(tlv::ParameterKey, "nonce")
-              );
+      response.push_back(makeStringBlock(tlv::ParameterKey, "nonce"));
       auto nonce = fromHex(request.challengeState->secrets.get("nonce", ""));
-      response.push_back(
-              makeBinaryBlock(tlv::ParameterValue, nonce->data(), 16)
-            );
+      response.push_back(makeBinaryBlock(tlv::ParameterValue, nonce->data(), 16));
     }
   }
   if (!issuedCertName.empty()) {
@@ -50,14 +46,16 @@
   response.encode();
   return encodeBlockWithAesGcm128(ndn::tlv::Content, request.encryptionKey.data(),
                                   response.value(), response.value_size(),
-                                  request.requestId.data(), request.requestId.size(), request.aesBlockCounter);
+                                  request.requestId.data(), request.requestId.size(),
+                                  request.encryptionIv);
 }
 
 void
 challengetlv::decodeDataContent(const Block& contentBlock, requester::RequestState& state)
 {
   auto result = decodeBlockWithAesGcm128(contentBlock, state.aesKey.data(),
-                                         state.requestId.data(), state.requestId.size());
+                                         state.requestId.data(), state.requestId.size(),
+                                         state.decryptionIv);
   auto data = makeBinaryBlock(tlv::EncryptedPayload, result.data(), result.size());
   data.parse();
   state.status = statusFromBlock(data.get(tlv::Status));
@@ -68,13 +66,15 @@
     state.remainingTries = readNonNegativeInteger(data.get(tlv::RemainingTries));
   }
   if (data.find(tlv::RemainingTime) != data.elements_end()) {
-    state.freshBefore = time::system_clock::now() + time::seconds(readNonNegativeInteger(data.get(tlv::RemainingTime)));
+    state.freshBefore = time::system_clock::now() +
+                        time::seconds(readNonNegativeInteger(data.get(tlv::RemainingTime)));
   }
   if (data.find(tlv::IssuedCertName) != data.elements_end()) {
     Block issuedCertNameBlock = data.get(tlv::IssuedCertName);
     state.issuedCertName = Name(issuedCertNameBlock.blockFromValue());
   }
-  if (data.find(tlv::ParameterKey) != data.elements_end() && readString(data.get(tlv::ParameterKey)) == "nonce") {
+  if (data.find(tlv::ParameterKey) != data.elements_end() &&
+      readString(data.get(tlv::ParameterKey)) == "nonce") {
     if (data.find(tlv::ParameterKey) == data.elements_end()) {
         NDN_THROW(std::runtime_error("Parameter Key found, but no value found"));
     }
diff --git a/src/detail/crypto-helpers.cpp b/src/detail/crypto-helpers.cpp
index cc09a95..469a967 100644
--- a/src/detail/crypto-helpers.cpp
+++ b/src/detail/crypto-helpers.cpp
@@ -325,60 +325,73 @@
   }
 }
 
-Block
-encodeBlockWithAesGcm128(uint32_t tlvType, const uint8_t* key, const uint8_t* payload, size_t payloadSize,
-                         const uint8_t* associatedData, size_t associatedDataSize, uint32_t& counter)
+static void
+updateIv(std::vector<uint8_t>& iv, size_t payloadSize)
 {
-  Buffer iv(12);
-  random::generateSecureBytes(iv.data(), 8);
-  if (tlvType == ndn::tlv::ApplicationParameters) {
-    // requester
-    iv[0] &= ~(1UL << 7);
-  }
-  else {
-    // CA
-    iv[0] |= 1UL << 7;
-  }
-  uint32_t temp = boost::endian::native_to_big(counter);
-  std::memcpy(&iv[8], reinterpret_cast<const uint8_t*>(&temp), 4);
+  uint32_t counter = boost::endian::load_big_u32(&iv[8]);
   uint32_t increment = (payloadSize + 15) / 16;
-  if (std::numeric_limits<uint32_t>::max() - counter < increment) {
+  if (std::numeric_limits<uint32_t>::max() - counter <= increment) {
     NDN_THROW(std::runtime_error("Error incrementing the AES block counter: "
                                  "too many blocks have been encrypted for the same request instance"));
   }
   else {
     counter += increment;
   }
+  boost::endian::store_big_u32(&iv[8], counter);
+}
 
+Block
+encodeBlockWithAesGcm128(uint32_t tlvType, const uint8_t* key, const uint8_t* payload, size_t payloadSize,
+                         const uint8_t* associatedData, size_t associatedDataSize, std::vector<uint8_t>& encryptionIv)
+{
   // The spec of AES encrypted payload TLV used in NDNCERT:
   //   https://github.com/named-data/ndncert/wiki/NDNCERT-Protocol-0.3#242-aes-gcm-encryption
   Buffer encryptedPayload(payloadSize);
   uint8_t tag[16];
+  if (encryptionIv.empty()) {
+    encryptionIv.resize(12, 0);
+    random::generateSecureBytes(encryptionIv.data(), 8);
+  }
   size_t encryptedPayloadLen = aesGcm128Encrypt(payload, payloadSize, associatedData, associatedDataSize,
-                                                key, iv.data(), encryptedPayload.data(), tag);
+                                                key, encryptionIv.data(), encryptedPayload.data(), tag);
   Block content(tlvType);
-  content.push_back(makeBinaryBlock(tlv::InitializationVector, iv.data(), iv.size()));
+  content.push_back(makeBinaryBlock(tlv::InitializationVector, encryptionIv.data(), encryptionIv.size()));
   content.push_back(makeBinaryBlock(tlv::AuthenticationTag, tag, 16));
   content.push_back(makeBinaryBlock(tlv::EncryptedPayload, encryptedPayload.data(), encryptedPayloadLen));
   content.encode();
+  // update IV's counter
+  updateIv(encryptionIv, payloadSize);
   return content;
 }
 
 Buffer
-decodeBlockWithAesGcm128(const Block& block, const uint8_t* key, const uint8_t* associatedData, size_t associatedDataSize)
+decodeBlockWithAesGcm128(const Block& block, const uint8_t* key,
+                         const uint8_t* associatedData, size_t associatedDataSize,
+                         std::vector<uint8_t>& decryptionIv)
 {
   // The spec of AES encrypted payload TLV used in NDNCERT:
   //   https://github.com/named-data/ndncert/wiki/NDNCERT-Protocol-0.3#242-aes-gcm-encryption
   block.parse();
   const auto& encryptedPayloadBlock = block.get(tlv::EncryptedPayload);
   Buffer result(encryptedPayloadBlock.value_size());
+  std::vector<uint8_t> currentIv(block.get(tlv::InitializationVector).value(), block.get(tlv::InitializationVector).value() + 12);
+  if (decryptionIv.empty()) {
+    decryptionIv = currentIv;
+  }
+  else {
+    if (currentIv != decryptionIv) {
+      NDN_THROW(std::runtime_error("Error when decrypting the AES Encrypted Block: "
+                                   "The observed IV is incorrectly formed."));
+    }
+  }
   auto resultLen = aesGcm128Decrypt(encryptedPayloadBlock.value(), encryptedPayloadBlock.value_size(),
                                     associatedData, associatedDataSize, block.get(tlv::AuthenticationTag).value(),
-                                    key, block.get(tlv::InitializationVector).value(), result.data());
+                                    key, currentIv.data(), result.data());
   if (resultLen != encryptedPayloadBlock.value_size()) {
     NDN_THROW(std::runtime_error("Error when decrypting the AES Encrypted Block: "
                                  "Decrypted payload is of an unexpected size"));
   }
+  updateIv(decryptionIv, resultLen);
   return result;
 }
 
diff --git a/src/detail/crypto-helpers.hpp b/src/detail/crypto-helpers.hpp
index 56446e4..801c3a3 100644
--- a/src/detail/crypto-helpers.hpp
+++ b/src/detail/crypto-helpers.hpp
@@ -144,13 +144,15 @@
  * @param payloadSize The size of the plaintext payload.
  * @param associatedData The associated data used for authentication.
  * @param associatedDataSize The size of associated data.
- * @param counter An opaque counter that must be passed to subsequent invocations of this function
- *                with the same @p key.
+ * @param last The IV used by the last AES encryption and is needed by subsequent invocations of this function
+ *             with the same @p key.
  * @return Block The TLV block with @p tlv_type TLV TYPE.
  */
 Block
-encodeBlockWithAesGcm128(uint32_t tlvType, const uint8_t* key, const uint8_t* payload, size_t payloadSize,
-                         const uint8_t* associatedData, size_t associatedDataSize, uint32_t& counter);
+encodeBlockWithAesGcm128(uint32_t tlvType, const uint8_t* key,
+                         const uint8_t* payload, size_t payloadSize,
+                         const uint8_t* associatedData, size_t associatedDataSize,
+                         std::vector<uint8_t>& lastIv);
 
 /**
  * @brief Decode the payload from TLV block with Authenticated GCM 128 Encryption.
@@ -161,11 +163,14 @@
  * @param key The AES key used for encryption.
  * @param associatedData The associated data used for authentication.
  * @param associatedDataSize The size of associated data.
+ * @param decryptionIv The IV observed in the last AES decryption and is needed by subsequent
+ *                     invocations of this function with the same @p key.
  * @return Buffer The plaintext buffer.
  */
 Buffer
 decodeBlockWithAesGcm128(const Block& block, const uint8_t* key,
-                         const uint8_t* associatedData, size_t associatedDataSize);
+                         const uint8_t* associatedData, size_t associatedDataSize,
+                         std::vector<uint8_t>& decryptionIv);
 
 } // namespace ndncert
 } // namespace ndn