Prepare for testbed deployment: name assignment and redirection policy

Change-Id: I7f4da10b763f3891d33820e9c6f4c7cb0eea60ce
diff --git a/src/detail/ca-configuration.cpp b/src/detail/ca-configuration.cpp
index a6d26a0..55b8a7c 100644
--- a/src/detail/ca-configuration.cpp
+++ b/src/detail/ca-configuration.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2017-2021, Regents of the University of California.
+ * Copyright (c) 2017-2022, Regents of the University of California.
  *
  * This file is part of ndncert, a certificate management system based on NDN.
  *
@@ -53,11 +53,24 @@
       auto caPrefixStr = item.second.get(CONFIG_CA_PREFIX, "");
       auto caCertStr = item.second.get(CONFIG_CERTIFICATE, "");
       if (caCertStr == "") {
-        NDN_THROW(std::runtime_error("Redirect-to item's ca-prefix or certificate cannot be empty."));
+        NDN_THROW(std::runtime_error("Redirect-to item's certificate cannot be empty."));
       }
       std::istringstream ss(caCertStr);
       auto caCert = ndn::io::load<Certificate>(ss);
-      redirection.push_back(caCert);
+      if (caPrefixStr != "" && Name(caPrefixStr) != caCert->getIdentity()) {
+        NDN_THROW(std::runtime_error("Redirect-to item's prefix and certificate does not match."));
+      }
+
+      auto policyType = item.second.get(CONFIG_REDIRECTION_POLICY_TYPE, "");
+      auto policyParam = item.second.get(CONFIG_REDIRECTION_POLICY_PARAM, "");
+      if (policyType.empty()) {
+          NDN_THROW(std::runtime_error("Redirect-to policy type expected but not provided."));
+      }
+      auto policy = RedirectionPolicy::createPolicyFunc(policyType, policyParam);
+      if (policy == nullptr) {
+        NDN_THROW(std::runtime_error("Error on creating redirection policy"));
+      }
+      redirection.emplace_back(caCert, std::move(policy));
     }
   }
   // parse name assignment if appears
diff --git a/src/detail/ca-configuration.hpp b/src/detail/ca-configuration.hpp
index 2a2a9ff..0a02777 100644
--- a/src/detail/ca-configuration.hpp
+++ b/src/detail/ca-configuration.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2017-2021, Regents of the University of California.
+ * Copyright (c) 2017-2022, Regents of the University of California.
  *
  * This file is part of ndncert, a certificate management system based on NDN.
  *
@@ -21,8 +21,9 @@
 #ifndef NDNCERT_DETAIL_CA_CONFIGURATION_HPP
 #define NDNCERT_DETAIL_CA_CONFIGURATION_HPP
 
-#include "detail/ca-profile.hpp"
 #include "name-assignment/assignment-func.hpp"
+#include "redirection/redirection-policy.hpp"
+#include "ca-profile.hpp"
 
 namespace ndncert {
 namespace ca {
@@ -66,7 +67,7 @@
   /**
    * @brief Used for CA redirection
    */
-  std::vector<std::shared_ptr<Certificate>> redirection;
+  std::vector<std::pair<std::shared_ptr<Certificate>, std::unique_ptr<RedirectionPolicy>>> redirection;
   /**
    * @brief Name Assignment Functions
    */
diff --git a/src/detail/ca-profile.hpp b/src/detail/ca-profile.hpp
index 0592be5..74af5c0 100644
--- a/src/detail/ca-profile.hpp
+++ b/src/detail/ca-profile.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2017-2021, Regents of the University of California.
+ * Copyright (c) 2017-2022, Regents of the University of California.
  *
  * This file is part of ndncert, a certificate management system based on NDN.
  *
@@ -37,6 +37,8 @@
 const std::string CONFIG_CERTIFICATE = "certificate";
 const std::string CONFIG_REDIRECTION = "redirect-to";
 const std::string CONFIG_NAME_ASSIGNMENT = "name-assignment";
+const std::string CONFIG_REDIRECTION_POLICY_TYPE = "policy-type";
+const std::string CONFIG_REDIRECTION_POLICY_PARAM = "policy-param";
 
 class CaProfile
 {
diff --git a/src/detail/challenge-encoder.cpp b/src/detail/challenge-encoder.cpp
index 5d9d46b..0f85fdc 100644
--- a/src/detail/challenge-encoder.cpp
+++ b/src/detail/challenge-encoder.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2017-2021, Regents of the University of California.
+ * Copyright (c) 2017-2022, Regents of the University of California.
  *
  * This file is part of ndncert, a certificate management system based on NDN.
  *
@@ -59,31 +59,62 @@
   auto data = ndn::makeBinaryBlock(tlv::EncryptedPayload, result.data(), result.size());
   data.parse();
 
-  state.m_status = statusFromBlock(data.get(tlv::Status));
-  if (data.find(tlv::ChallengeStatus) != data.elements_end()) {
-    state.m_challengeStatus = readString(data.get(tlv::ChallengeStatus));
-  }
-  if (data.find(tlv::RemainingTries) != data.elements_end()) {
-    state.m_remainingTries = readNonNegativeInteger(data.get(tlv::RemainingTries));
-  }
-  if (data.find(tlv::RemainingTime) != data.elements_end()) {
-    state.m_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.m_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()) {
+  int numStatus = 0;
+  bool lookingForNonce = false;
+  for (const auto &item : data.elements()) {
+    if (!lookingForNonce) {
+      switch (item.type()) {
+        case tlv::Status:
+          state.m_status = statusFromBlock(data.get(tlv::Status));
+          numStatus++;
+          break;
+        case tlv::ChallengeStatus:
+          state.m_challengeStatus = readString(item);
+          break;
+        case tlv::RemainingTries:
+          state.m_remainingTries = readNonNegativeInteger(item);
+          break;
+        case tlv::RemainingTime:
+          state.m_freshBefore = time::system_clock::now() +
+                                time::seconds(readNonNegativeInteger(item));
+          break;
+        case tlv::IssuedCertName:
+          state.m_issuedCertName = Name(item.blockFromValue());
+          break;
+        case tlv::ParameterKey:
+          if (readString(item) == "nonce") {
+            lookingForNonce = true;
+          }
+          else {
+            NDN_THROW(std::runtime_error("Unknown Parameter: " + readString(item)));
+          }
+          break;
+        default:
+          if (ndn::tlv::isCriticalType(item.type())) {
+            NDN_THROW(std::runtime_error("Unrecognized TLV Type: " + std::to_string(item.type())));
+          }
+          else {
+            //ignore
+          }
+          break;
+      }
+    }
+    else {
+      if (item.type() == tlv::ParameterValue) {
+        lookingForNonce = false;
+        if (item.value_size() != 16) {
+          NDN_THROW(std::runtime_error("Wrong nonce length"));
+        }
+        memcpy(state.m_nonce.data(), item.value(), 16);
+      }
+      else {
         NDN_THROW(std::runtime_error("Parameter Key found, but no value found"));
+      }
     }
-    Block nonceBlock = data.get(tlv::ParameterValue);
-    if (nonceBlock.value_size() != 16) {
-        NDN_THROW(std::runtime_error("Wrong nonce length"));
-    }
-    memcpy(state.m_nonce.data(), nonceBlock.value(), 16);
+  }
+  if (numStatus != 1) {
+    NDN_THROW(std::runtime_error("number of status block is not equal to 1; there are " +
+                                 std::to_string(numStatus) + " status blocks"));
   }
 }
 
diff --git a/src/detail/error-encoder.cpp b/src/detail/error-encoder.cpp
index e1e4357..990ce84 100644
--- a/src/detail/error-encoder.cpp
+++ b/src/detail/error-encoder.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2017-2021, Regents of the University of California.
+ * Copyright (c) 2017-2022, Regents of the University of California.
  *
  * This file is part of ndncert, a certificate management system based on NDN.
  *
@@ -20,6 +20,8 @@
 
 #include "detail/error-encoder.hpp"
 
+NDN_LOG_INIT(ndncert.encode.error);
+
 namespace ndncert {
 
 Block
@@ -35,12 +37,44 @@
 std::tuple<ErrorCode, std::string>
 errortlv::decodefromDataContent(const Block& block)
 {
-  block.parse();
-  if (block.find(tlv::ErrorCode) == block.elements_end()) {
+  try {
+    block.parse();
+    int codeCount = 0;
+    int infoCount = 0;
+    int otherCriticalCount = 0;
+    ErrorCode error;
+    std::string errorInfo;
+    for (const auto& item : block.elements()) {
+      if (item.type() == tlv::ErrorCode) {
+        error = static_cast<ErrorCode>(readNonNegativeInteger(block.get(tlv::ErrorCode)));
+        codeCount ++;
+      }
+      else if (item.type() == tlv::ErrorInfo) {
+        errorInfo = readString(block.get(tlv::ErrorInfo));
+        infoCount ++;
+      }
+      else if (ndn::tlv::isCriticalType(item.type())) {
+        otherCriticalCount ++;
+      }
+      else {
+        //ignore
+      }
+    }
+    if (codeCount == 0 && infoCount == 0) {
+      return std::make_tuple(ErrorCode::NO_ERROR, "");
+    }
+    if (codeCount != 1 || infoCount != 1) {
+      NDN_THROW(std::runtime_error("Error TLV contains " + std::to_string(codeCount) + " error code(s) and " +
+                                      std::to_string(infoCount) + "error info(s), instead of expected 1 times each."));
+    }
+    if (otherCriticalCount > 0) {
+      NDN_THROW(std::runtime_error("Unknown Critical TLV type in error packet"));
+    }
+    return std::make_tuple(error, errorInfo);
+  } catch (const std::exception& e) {
+    NDN_LOG_ERROR("[errortlv::DecodeFromDataContent] Exception in error message decoding: " << e.what());
     return std::make_tuple(ErrorCode::NO_ERROR, "");
   }
-  ErrorCode error = static_cast<ErrorCode>(readNonNegativeInteger(block.get(tlv::ErrorCode)));
-  return std::make_tuple(error, readString(block.get(tlv::ErrorInfo)));
 }
 
 } // namespace ndncert
diff --git a/src/detail/error-encoder.hpp b/src/detail/error-encoder.hpp
index 3b17353..cd136cf 100644
--- a/src/detail/error-encoder.hpp
+++ b/src/detail/error-encoder.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2017-2021, Regents of the University of California.
+ * Copyright (c) 2017-2022, Regents of the University of California.
  *
  * This file is part of ndncert, a certificate management system based on NDN.
  *
diff --git a/src/detail/info-encoder.cpp b/src/detail/info-encoder.cpp
index e6e6581..9331f1f 100644
--- a/src/detail/info-encoder.cpp
+++ b/src/detail/info-encoder.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2017-2021, Regents of the University of California.
+ * Copyright (c) 2017-2022, Regents of the University of California.
  *
  * This file is part of ndncert, a certificate management system based on NDN.
  *
@@ -20,6 +20,8 @@
 
 #include "detail/info-encoder.hpp"
 
+NDN_LOG_INIT(ndncert.encode.info);
+
 namespace ndncert {
 
 Block
@@ -41,36 +43,41 @@
   content.push_back(ndn::makeNonNegativeIntegerBlock(tlv::MaxValidityPeriod, caConfig.maxValidityPeriod.count()));
   content.push_back(makeNestedBlock(tlv::CaCertificate, certificate));
   content.encode();
+  NDN_LOG_TRACE("Encoding INFO packet with certificate " << certificate.getFullName());
   return content;
 }
 
 CaProfile
-infotlv::decodeDataContent(const Block& block)
-{
+infotlv::decodeDataContent(const Block& block) {
   CaProfile result;
   block.parse();
-  for (auto const& item : block.elements()) {
+  for (auto const &item : block.elements()) {
     switch (item.type()) {
-    case tlv::CaPrefix:
-      item.parse();
-      result.caPrefix.wireDecode(item.get(ndn::tlv::Name));
-      break;
-    case tlv::CaInfo:
-      result.caInfo = readString(item);
-      break;
-    case tlv::ParameterKey:
-      result.probeParameterKeys.push_back(readString(item));
-      break;
-    case tlv::MaxValidityPeriod:
-      result.maxValidityPeriod = time::seconds(readNonNegativeInteger(item));
-      break;
-    case tlv::CaCertificate:
-      item.parse();
-      result.cert = std::make_shared<Certificate>(item.get(ndn::tlv::Data));
-      break;
-    default:
-      continue;
-      break;
+      case tlv::CaPrefix:
+        item.parse();
+        result.caPrefix.wireDecode(item.get(ndn::tlv::Name));
+        break;
+      case tlv::CaInfo:
+        result.caInfo = readString(item);
+        break;
+      case tlv::ParameterKey:
+        result.probeParameterKeys.push_back(readString(item));
+        break;
+      case tlv::MaxValidityPeriod:
+        result.maxValidityPeriod = time::seconds(readNonNegativeInteger(item));
+        break;
+      case tlv::CaCertificate:
+        item.parse();
+        result.cert = std::make_shared<Certificate>(item.get(ndn::tlv::Data));
+        break;
+      default:
+        if (ndn::tlv::isCriticalType(item.type())) {
+          NDN_THROW(std::runtime_error("Unrecognized TLV Type: " + std::to_string(item.type())));
+        }
+        else {
+          //ignore
+        }
+        break;
     }
   }
   return result;
diff --git a/src/detail/probe-encoder.cpp b/src/detail/probe-encoder.cpp
index bf51438..13b040e 100644
--- a/src/detail/probe-encoder.cpp
+++ b/src/detail/probe-encoder.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2017-2021, Regents of the University of California.
+ * Copyright (c) 2017-2022, Regents of the University of California.
  *
  * This file is part of ndncert, a certificate management system based on NDN.
  *
@@ -35,14 +35,21 @@
 }
 
 std::multimap<std::string, std::string>
-probetlv::decodeApplicationParameters(const Block& block)
-{
+probetlv::decodeApplicationParameters(const Block& block) {
   std::multimap<std::string, std::string> result;
   block.parse();
-  for (size_t i = 0; i < block.elements().size() - 1; i++) {
-    if (block.elements()[i].type() == tlv::ParameterKey && block.elements()[i + 1].type() == tlv::ParameterValue) {
-      result.emplace(readString(block.elements().at(i)), readString(block.elements().at(i + 1)));
-      i ++;
+  const auto& elements = block.elements();
+  for (size_t i = 0; i < elements.size(); i++) {
+    if (i + 1 < elements.size() && elements[i].type() == tlv::ParameterKey &&
+        elements[i + 1].type() == tlv::ParameterValue) {
+      result.emplace(readString(elements.at(i)), readString(elements.at(i + 1)));
+      i++;
+    }
+    else if (ndn::tlv::isCriticalType(elements[i].type())) {
+      NDN_THROW(std::runtime_error("Unrecognized TLV Type: " + std::to_string(elements[i].type())));
+    }
+    else {
+      //ignore
     }
   }
   return result;
@@ -50,7 +57,7 @@
 
 Block
 probetlv::encodeDataContent(const std::vector<Name>& identifiers, optional<size_t> maxSuffixLength,
-                            std::vector<std::shared_ptr<Certificate>> redirectionItems)
+                            std::vector<ndn::Name> redirectionItems)
 {
   Block content(ndn::tlv::Content);
   for (const auto& name : identifiers) {
@@ -61,9 +68,11 @@
     }
     content.push_back(item);
   }
+
   for (const auto& item : redirectionItems) {
-    content.push_back(makeNestedBlock(tlv::ProbeRedirect, item->getFullName()));
+    content.push_back(makeNestedBlock(tlv::ProbeRedirect, item));
   }
+
   content.encode();
   return content;
 }
@@ -71,8 +80,7 @@
 void
 probetlv::decodeDataContent(const Block& block,
                             std::vector<std::pair<Name, int>>& availableNames,
-                            std::vector<Name>& availableRedirection)
-{
+                            std::vector<Name>& availableRedirection) {
   block.parse();
   for (const auto& item : block.elements()) {
     if (item.type() == tlv::ProbeResponse) {
@@ -89,15 +97,27 @@
         else if (subBlock.type() == tlv::MaxSuffixLength) {
           maxSuffixLength = readNonNegativeInteger(subBlock);
         }
+        else if (ndn::tlv::isCriticalType(subBlock.type())) {
+          NDN_THROW(std::runtime_error("Unrecognized TLV Type in probe name item: " + std::to_string(subBlock.type())));
+        }
+        else {
+          //ignore
+        }
       }
       if (elementName.empty()) {
         NDN_THROW(std::runtime_error("Invalid probe format"));
       }
       availableNames.emplace_back(elementName, maxSuffixLength);
     }
-    if (item.type() == tlv::ProbeRedirect) {
+    else if (item.type() == tlv::ProbeRedirect) {
       availableRedirection.emplace_back(Name(item.blockFromValue()));
     }
+    else if (ndn::tlv::isCriticalType(item.type())) {
+      NDN_THROW(std::runtime_error("Unrecognized TLV Type: " + std::to_string(item.type())));
+    }
+    else {
+      //ignore
+    }
   }
 }
 
diff --git a/src/detail/probe-encoder.hpp b/src/detail/probe-encoder.hpp
index 6afe083..223c61b 100644
--- a/src/detail/probe-encoder.hpp
+++ b/src/detail/probe-encoder.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2017-2021, Regents of the University of California.
+ * Copyright (c) 2017-2022, Regents of the University of California.
  *
  * This file is part of ndncert, a certificate management system based on NDN.
  *
@@ -38,8 +38,7 @@
 Block
 encodeDataContent(const std::vector<Name>& identifiers,
                   optional<size_t> maxSuffixLength = nullopt,
-                  std::vector<std::shared_ptr<Certificate>> redirectionItems =
-                          std::vector<std::shared_ptr<Certificate>>());
+                  std::vector<ndn::Name> redirectionItems = std::vector<ndn::Name>());
 
 std::multimap<std::string, std::string>
 decodeApplicationParameters(const Block& block);
diff --git a/src/detail/request-encoder.cpp b/src/detail/request-encoder.cpp
index 30e2523..fde2d86 100644
--- a/src/detail/request-encoder.cpp
+++ b/src/detail/request-encoder.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2017-2021, Regents of the University of California.
+ * Copyright (c) 2017-2022, Regents of the University of California.
  *
  * This file is part of ndncert, a certificate management system based on NDN.
  *
@@ -51,20 +51,35 @@
 {
   payload.parse();
 
-  const auto& ecdhBlock = payload.get(tlv::EcdhPub);
-  ecdhPub.resize(ecdhBlock.value_size());
-  std::memcpy(ecdhPub.data(), ecdhBlock.value(), ecdhBlock.value_size());
-
+  int ecdhPubCount = 0;
   Block requestPayload;
-  if (requestType == RequestType::NEW) {
-    requestPayload = payload.get(tlv::CertRequest);
+  int requestPayloadCount = 0;
+  for (const auto &item : payload.elements()) {
+    if (item.type() == tlv::EcdhPub) {
+      ecdhPub.resize(item.value_size());
+      std::memcpy(ecdhPub.data(), item.value(), item.value_size());
+      ecdhPubCount++;
+    }
+    else if ((requestType == RequestType::NEW && item.type() == tlv::CertRequest) ||
+               (requestType == RequestType::REVOKE && item.type() == tlv::CertToRevoke)) {
+      requestPayload = item;
+      requestPayloadCount++;
+      requestPayload.parse();
+      clientCert = std::make_shared<Certificate>(requestPayload.get(ndn::tlv::Data));
+    }
+    else if (ndn::tlv::isCriticalType(item.type())) {
+      NDN_THROW(std::runtime_error("Unrecognized TLV Type: " + std::to_string(item.type())));
+    }
+    else {
+      //ignore
+    }
   }
-  else if (requestType == RequestType::REVOKE) {
-    requestPayload = payload.get(tlv::CertToRevoke);
-  }
-  requestPayload.parse();
 
-  clientCert = std::make_shared<Certificate>(requestPayload.get(ndn::tlv::Data));
+  if (ecdhPubCount != 1 || requestPayloadCount != 1) {
+    NDN_THROW(std::runtime_error("Error TLV contains " + std::to_string(ecdhPubCount) + " ecdh public param(s) and " +
+                                 std::to_string(requestPayloadCount) +
+                                 "request payload(s), instead of expected 1 times each."));
+  }
 }
 
 Block
@@ -85,25 +100,38 @@
 
 std::list <std::string>
 requesttlv::decodeDataContent(const Block& content, std::vector <uint8_t>& ecdhKey,
-                              std::array<uint8_t, 32>& salt, RequestId& requestId)
-{
+                              std::array<uint8_t, 32>& salt, RequestId& requestId) {
+  std::list<std::string> challenges;
   content.parse();
-
-  const auto& ecdhBlock = content.get(tlv::EcdhPub);
-  ecdhKey.resize(ecdhBlock.value_size());
-  std::memcpy(ecdhKey.data(), ecdhBlock.value(), ecdhBlock.value_size());
-
-  const auto& saltBlock = content.get(tlv::Salt);
-  std::memcpy(salt.data(), saltBlock.value(), saltBlock.value_size());
-
-  const auto& requestIdBlock = content.get(tlv::RequestId);
-  std::memcpy(requestId.data(), requestIdBlock.value(), requestIdBlock.value_size());
-
-  std::list <std::string> challenges;
-  for (auto const& element : content.elements()) {
+  int ecdhPubCount = 0, saltCount = 0, requestIdCount = 0;
+  for (auto const &element : content.elements()) {
     if (element.type() == tlv::Challenge) {
       challenges.push_back(readString(element));
     }
+    else if (element.type() == tlv::EcdhPub) {
+      ecdhKey.resize(element.value_size());
+      std::memcpy(ecdhKey.data(), element.value(), element.value_size());
+      ecdhPubCount++;
+    }
+    else if (element.type() == tlv::Salt) {
+      std::memcpy(salt.data(), element.value(), element.value_size());
+      saltCount++;
+    }
+    else if (element.type() == tlv::RequestId) {
+      std::memcpy(requestId.data(), element.value(), element.value_size());
+      requestIdCount++;
+    }
+    else if (ndn::tlv::isCriticalType(element.type())) {
+      NDN_THROW(std::runtime_error("Unrecognized TLV Type: " + std::to_string(element.type())));
+    }
+    else {
+      //ignore
+    }
+  }
+  if (ecdhPubCount != 1 || saltCount != 1 || requestIdCount != 1) {
+    NDN_THROW(std::runtime_error("Error TLV contains " + std::to_string(ecdhPubCount) + " ecdh public param(s), " +
+                                 std::to_string(saltCount) + " salt(s) and " + std::to_string(requestIdCount) +
+                                 "request id(s), instead of expected 1 times each."));
   }
   return challenges;
 }