Prepare for testbed deployment: name assignment and redirection policy

Change-Id: I7f4da10b763f3891d33820e9c6f4c7cb0eea60ce
diff --git a/ca.conf.sample b/ca.conf.sample
index de5be8f..a2ca7e1 100644
--- a/ca.conf.sample
+++ b/ca.conf.sample
@@ -14,9 +14,15 @@
   ],
   "redirect-to":
   [
-    {
-      "ca-prefix": "/example/site1",
-      "certificate": "Bv0CvQcwCAdleGFtcGxlCAVzaXRlMQgDS0VZCAh6af3szF4QZwgEc2VsZggJ/QAAAXT6+NCKFAkYAQIZBAA27oAV/QEmMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA43hjZT0HUFFJcqwj8lZnd/vg0NrzqvZ4jhsq1c+nv6J3Huc9Uq5jRZwhFQ8nBWT3CeFScO5FUQfNXDIDncZ4vYPFEnockOFVtvmKQ/ELwReUvjH80d1NPGrIVrD0lMRpv2sFr6NW2p7aw6bCSj3OJq7H/+QHDkAryssMZyHwTbPzMZHyYKmxR68CyCCpvLlgp8tYFT+cCrOc3lz3nROK3VFR+apgwubpvl8nbKD10QLcgMHSkLoLEy/Ksq8OH7MQhUEZDjLk/zL9baZ7MiKXtdUZCNTZk13y5z+4aT4TqumLB+obiDXmv6JAi+CkYIMf2ck2IvMV6JgxxIlv3+Ke2wIDAQABFlAbAQEcIQcfCAdleGFtcGxlCAVzaXRlMQgDS0VZCAh6af3szF4QZ/0A/Sb9AP4PMTk3MDAxMDFUMDAwMDAw/QD/DzIwNDAwOTMwVDIyNTQwNBf9AQCCSXOqUX40mAIdKCa+nnfJCGZbNowQPJp5kDnyolj5/Ek9x8czyLcX58xTsgYtiPmL5DxMgkRujRJu9INm0pUJIJRlsqhDOwsrxIjlSgwy5AeexYe7SM3rSwljLxTR4MfBw26pym9iYt8ovHXotCDE+etyKwHzXoOgzxORoPXqBGwobNOPnhDfpzHQBFOrPd8qqLAGioNNk/k2U/uyvBbLoZS4ScNVJpfbcvcmzu/A8H/VyT4234LrlISL9WpWlO8J18yzhrXchFR0ZwCoYge5rLZ4vsQhY1WqXHCsYnRa3la6Txz44EWYEBpmk12qnkPt06KAPvQ82N1CICxFb9NY"
-    }
+      {
+        "ca-prefix": "/ndn/edu/ucla",
+        "certificate": "Bv0BNQcwCANuZG4IA2VkdQgEdWNsYQgDS0VZCAgAdGt6D7S2VAgEc2VsZggJ/QAAAX5lZMOiFAkYAQIZBAA27oAVWzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABOKmvwHmK5t+MhMPgft4qmKC7YF9I6UM/o7GFa4BjZQknsqLvxdW2zIAF+iPPHJV0eVAijX6bYrQobuomiWZAY0WUBsBAxwhBx8IA25kbggDZWR1CAR1Y2xhCANLRVkICAB0a3oPtLZU/QD9Jv0A/g8xOTcwMDEwMVQwMDAwMDD9AP8PMjA0MjAxMTJUMDAxNjQ5F0cwRQIgBF/HS0j1DMo/dIILv/6IMUmMAhVtS3m97YgS8tsBhC0CIQCgEm0e6KoBCyV6PiueN9YW9zSSkdg8MLCxsyduP8tRsQ=="
+      },
+      {
+        "ca-prefix": "/ndn/edu/ucla/cs",
+        "certificate": "Bv0BPgc0CANuZG4IA2VkdQgEdWNsYQgCY3MIA0tFWQgI27kFrpVyxUAIBHNlbGYICf0AAAF+ZZ/79xQJGAECGQQANu6AFVswWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASOLtEWMpMk8tPqPe0VY9SAYA0e969NNy5t0QeseNvr6AbYWQHBR4oa6Ymv3TRlQnyy+IzvKPte5suX/Qhtnjn2FlQbAQMcJQcjCANuZG4IA2VkdQgEdWNsYQgCY3MIA0tFWQgI27kFrpVyxUD9AP0m/QD+DzE5NzAwMTAxVDAwMDAwMP0A/w8yMDQyMDExMlQwMTIxMzAXSDBGAiEAm+aJbcmI0n37Qhear5fo//S02ZlDkmao8a7olSsElx8CIQDD8dZkYfD8xcvYl3vXm7G/NSXFrnrRqxC7NR/4r4swbw=="
+        "policy-type": "email",
+        "policy-param": "cs.ucla.edu"
+      }
   ]
 }
\ No newline at end of file
diff --git a/client.conf.sample b/client.conf.sample
index 3a06d7a..53dbd95 100644
--- a/client.conf.sample
+++ b/client.conf.sample
@@ -2,15 +2,16 @@
   "ca-list":
   [
     {
-      "ca-prefix": "/example",
-      "ca-info": "An example NDNCERT CA",
+      "ca-prefix": "/ndncert-demo",
+      "ca-info": "NDN Testbed NDNCERT CA (Demo)",
       "max-validity-period": "1296000",
-      "max-suffix-length": "2",
-      "probe-parameters":
-      [
-        {"probe-parameter-key": "email"}
+      "max-suffix-length": "5",
+      "probe-parameters": [
+        {
+          "probe-parameter-key": "email"
+        }
       ],
-      "certificate": "Bv0CrwcpCAdleGFtcGxlCANLRVkICJev38tR696CCARzZWxmCAn9AAABdPr4U3cUCRgBAhkEADbugBX9ASYwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDLjjKYDiimVlQD2vOAOYvu3iCsfS5mzQYOjw1cVS7+EDzC0T841FrmLEtrYe1USBGTSYUKNePspBTDMeAh2WlG+UmnzHyTJEcpv3Dtp/37pYIFlR8vW6TvBkBmCKVR7IDOW0DmBEY2iHbWOCCTyTd9NVyH4XiHqFx+cL9uGRyhVeXcZ5pl335SrnW58Q6tvZeOHS8YXuiAZ8E10Mdhzdmponl2+yRlc1kzkcAX1DQZt7eYXjlSsa5FSphLzw5LNqJuESLcrPRctN+4vs8xTIvWGex8n3+6wAhfXD9DLsO01mJorUxms+mwIKnV7KNLXtwDNsAZYT2BPPnO0H5Zk+tRAgMBAAEWSRsBARwaBxgIB2V4YW1wbGUIA0tFWQgIl6/fy1Hr3oL9AP0m/QD+DzE5NzAwMTAxVDAwMDAwMP0A/w8yMDQwMDkzMFQyMjUzMzIX/QEAZLgyhRsOERTgy5Q2X4FLXQV+r6emud472za0CVT20XHLXofkgybvLvY0UJ80CtuLcNRt/WKTDIKd01SoCnonx1VydKjXsO23RV95pBt/BqZVWakYEJEgbO5KzekpBHbKJmTOWBa/Tmgd9Pd5KFhQm9Ny2ZK1nlyyV37EKqR9jADkDVgRs+Sgr+Z4v14+WePROk7LR11P4NxYfgy2L0CHjpIxiFxnU+CpQL+BoGRQDTFgGq4gxUR45rpt/Y/vl5ImMdmIFDOI5W34AjVIhSLrYH3EDhWgjo9cDqvS+KT45i+t87SBQjtcQbrLl8osQcG+HgRldBv7HEEDVoPL8qL0yg=="
+      "certificate": "Bv0BLwctCAxuZG5jZXJ0LWRlbW8IA0tFWQgI92c9QI8lJGoIBHNlbGY2CAAAAX7YsardFAkYAQIZBAA27oAVWzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABNIu6i4dFU2FwW9641O4XehbwZD3INSgsEehPfZVRnDhQ0l5oRdUGJo6OQhOutExKov7dp+hzmRSAHbiO3dRjdIWThsBAxwfBx0IDG5kbmNlcnQtZGVtbwgDS0VZCAj3Zz1AjyUkav0A/Sb9AP4PMTk3MDAxMDFUMDAwMDAw/QD/DzIwNDIwMjAzVDA5MzcwORdGMEQCIA7CEGlG+KQnJudjqpCzt7IlHRgPV2ni1k5mSu0yZXKtAiAGkqnGwTu1e8LVqZ1XIidt/wDzcYXTQGEoltwhcN6jUw=="
     }
   ]
 }
diff --git a/ndncert-send-email-challenge.py b/ndncert-send-email-challenge.py
index 61d16ff..20198fc 100755
--- a/ndncert-send-email-challenge.py
+++ b/ndncert-send-email-challenge.py
@@ -1,12 +1,9 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 import sys
 import smtplib
 import argparse
 import socket
-try: # python3
-    from configparser import ConfigParser
-except ImportError: # python2
-    from ConfigParser import SafeConfigParser as ConfigParser
+from configparser import ConfigParser
 from email.mime.multipart import MIMEMultipart
 from email.mime.text import MIMEText
 
diff --git a/src/ca-module.cpp b/src/ca-module.cpp
index c2072dd..73582a8 100644
--- a/src/ca-module.cpp
+++ b/src/ca-module.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.
  *
@@ -153,25 +153,40 @@
 }
 
 void
-CaModule::onProbe(const Interest& request)
-{
+CaModule::onProbe(const Interest& request) {
   // PROBE Naming Convention: /<CA-Prefix>/CA/PROBE/[ParametersSha256DigestComponent]
   NDN_LOG_TRACE("Received PROBE request");
 
   // process PROBE requests: collect probe parameters
-  auto parameters = probetlv::decodeApplicationParameters(request.getApplicationParameters());
+  std::vector<ndn::Name> redirectionNames;
   std::vector<ndn::PartialName> availableComponents;
-  for (auto& item : m_config.nameAssignmentFuncs) {
-    auto names = item->assignName(parameters);
-    availableComponents.insert(availableComponents.end(), names.begin(), names.end());
+  try {
+    auto parameters = probetlv::decodeApplicationParameters(request.getApplicationParameters());
+
+    //collect redirections
+    for (auto &item : m_config.redirection) {
+      if (item.second->isRedirecting(parameters)) {
+        redirectionNames.push_back(item.first->getFullName());
+      }
+    }
+
+    //collect name assignments
+    for (auto &item : m_config.nameAssignmentFuncs) {
+      auto names = item->assignName(parameters);
+      availableComponents.insert(availableComponents.end(), names.begin(), names.end());
+    }
+  } catch (const std::exception& e) {
+    NDN_LOG_ERROR("[CaModule::onProbe]Error in decoding TLV: " << e.what());
+    return;
   }
-  if (availableComponents.size() == 0) {
+
+  if (availableComponents.size() == 0 && redirectionNames.size() == 0) {
     m_face.put(generateErrorDataPacket(request.getName(), ErrorCode::INVALID_PARAMETER,
                                        "Cannot generate available names from parameters provided."));
     return;
   }
-  std::vector <Name> availableNames;
-  for (const auto& component : availableComponents) {
+  std::vector<Name> availableNames;
+  for (const auto &component : availableComponents) {
     Name newIdentityName = m_config.caProfile.caPrefix;
     newIdentityName.append(component);
     availableNames.push_back(newIdentityName);
@@ -180,7 +195,7 @@
   Data result;
   result.setName(request.getName());
   result.setContent(
-    probetlv::encodeDataContent(availableNames, m_config.caProfile.maxSuffixLength, m_config.redirection));
+      probetlv::encodeDataContent(availableNames, m_config.caProfile.maxSuffixLength, redirectionNames));
   result.setFreshnessPeriod(DEFAULT_DATA_FRESHNESS_PERIOD);
   m_keyChain.sign(result, signingByIdentity(m_config.caProfile.caPrefix));
   m_face.put(result);
@@ -457,7 +472,7 @@
   Certificate newCert;
 
   Name certName = requestState.cert.getKeyName();
-  certName.append("NDNCERT").append(ndn::to_string(ndn::random::generateSecureWord64()));
+  certName.append("NDNCERT").appendVersion();
   newCert.setName(certName);
   newCert.setContent(requestState.cert.getContent());
   NDN_LOG_TRACE("cert request content " << requestState.cert);
diff --git a/src/challenge/challenge-email.cpp b/src/challenge/challenge-email.cpp
index bc624bc..ea0345c 100644
--- a/src/challenge/challenge-email.cpp
+++ b/src/challenge/challenge-email.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.
  *
@@ -29,7 +29,6 @@
 
 const std::string ChallengeEmail::NEED_CODE = "need-code";
 const std::string ChallengeEmail::WRONG_CODE = "wrong-code";
-const std::string ChallengeEmail::INVALID_EMAIL = "invalid-email";
 const std::string ChallengeEmail::PARAMETER_KEY_EMAIL = "email";
 const std::string ChallengeEmail::PARAMETER_KEY_CODE = "code";
 
@@ -50,13 +49,9 @@
   if (request.status == Status::BEFORE_CHALLENGE) {
     // for the first time, init the challenge
     std::string emailAddress = readString(params.get(tlv::ParameterValue));
-    if (!isValidEmailAddress(emailAddress)) {
-      return returnWithNewChallengeStatus(request, INVALID_EMAIL, JsonSection(), m_maxAttemptTimes - 1,
-                                          m_secretLifetime);
-    }
     auto lastComponentRequested = readString(request.cert.getIdentity().get(-1));
     if (lastComponentRequested != emailAddress) {
-      NDN_LOG_TRACE("Email and requested name do not match. Email " << emailAddress << "requested last component "
+      NDN_LOG_TRACE("Email and requested name do not match. Email " << emailAddress << " requested last component "
                     << lastComponentRequested);
     }
     std::string emailCode = generateSecretCode();
@@ -96,8 +91,8 @@
       }
       else {
         // run out times
-        NDN_LOG_TRACE("Wrong secret code provided. Ran out tires. Challenge failed.");
-        return returnWithError(request, ErrorCode::OUT_OF_TRIES, "Ran out tires.");
+        NDN_LOG_TRACE("Wrong secret code provided. Ran out of tries. Challenge failed.");
+        return returnWithError(request, ErrorCode::OUT_OF_TRIES, "Ran out of tries.");
       }
     }
   }
@@ -112,9 +107,6 @@
   if (status == Status::BEFORE_CHALLENGE && challengeStatus == "") {
     result.emplace(PARAMETER_KEY_EMAIL, "Please input your email address");
   }
-  else if (status == Status::CHALLENGE && challengeStatus == INVALID_EMAIL) {
-    result.emplace(PARAMETER_KEY_EMAIL, "Invalid email, please try again");
-  }
   else if (status == Status::CHALLENGE && challengeStatus == NEED_CODE) {
     result.emplace(PARAMETER_KEY_CODE, "Please input your verification code");
   }
@@ -176,8 +168,10 @@
   if (child.exit_code() != 0) {
     NDN_LOG_TRACE("EmailSending Script " + m_sendEmailScript + " fails.");
   }
-  NDN_LOG_TRACE("EmailSending Script " + m_sendEmailScript +
-             " was executed successfully with return value 0.");
+  else {
+    NDN_LOG_TRACE("EmailSending Script " + m_sendEmailScript +
+              " was executed successfully with return value 0.");
+  }
 }
 
 } // namespace ndncert
diff --git a/src/challenge/challenge-email.hpp b/src/challenge/challenge-email.hpp
index c5d76de..6de8216 100644
--- a/src/challenge/challenge-email.hpp
+++ b/src/challenge/challenge-email.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.
  *
@@ -44,7 +44,6 @@
  *
  * Failure info when application fails:
  *   FAILURE_MAXRETRY: When run out retry times.
- *   FAILURE_INVALID_EMAIL: When the email is invalid.
  *   FAILURE_TIMEOUT: When the secret lifetime expires.
  */
 class ChallengeEmail : public ChallengeModule
@@ -69,7 +68,6 @@
   // challenge status
   static const std::string NEED_CODE;
   static const std::string WRONG_CODE;
-  static const std::string INVALID_EMAIL;
   // challenge parameters
   static const std::string PARAMETER_KEY_EMAIL;
   static const std::string PARAMETER_KEY_CODE;
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;
 }
diff --git a/src/name-assignment/assignment-email.cpp b/src/name-assignment/assignment-email.cpp
new file mode 100644
index 0000000..685a820
--- /dev/null
+++ b/src/name-assignment/assignment-email.cpp
@@ -0,0 +1,62 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2017-2022, Regents of the University of California.
+ *
+ * This file is part of ndncert, a certificate management system based on NDN.
+ *
+ * ndncert is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * ndncert is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License along with
+ * ndncert, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndncert authors and contributors.
+ */
+
+#include "assignment-email.hpp"
+
+namespace ndncert {
+
+NDNCERT_REGISTER_FUNCFACTORY(AssignmentEmail, "email");
+
+AssignmentEmail::AssignmentEmail(const std::string& format)
+  : NameAssignmentFunc(format)
+{
+}
+
+std::vector<ndn::PartialName>
+AssignmentEmail::assignName(const std::multimap<std::string, std::string>& params)
+{
+  std::vector<ndn::PartialName> resultList;
+  Name result;
+  if (!m_nameFormat.empty() && params.count("email") > 0) {
+    const std::string& email = params.begin()->second;
+    auto formatIter = m_nameFormat.begin();
+    size_t emailSplit = email.rfind("@");
+    std::string domain = "." + email.substr(emailSplit + 1);
+
+    if (emailSplit != std::string::npos && emailSplit > 0) {
+      size_t domainSplit = domain.rfind(".");
+      while (domainSplit != std::string::npos) {
+        if (formatIter != m_nameFormat.end() && domain.substr(domainSplit + 1) == *formatIter) {
+          formatIter++;
+        }
+        else {
+          result.push_back(domain.substr(domainSplit + 1).c_str());
+        }
+        domain = domain.substr(0, domainSplit);
+        domainSplit = domain.rfind(".");
+      }
+      result.push_back(email.substr(0, emailSplit).c_str());
+      resultList.push_back(std::move(result));
+    }
+  }
+  return resultList;
+}
+
+} // namespace ndncert
diff --git a/src/name-assignment/assignment-email.hpp b/src/name-assignment/assignment-email.hpp
new file mode 100644
index 0000000..c0bb330
--- /dev/null
+++ b/src/name-assignment/assignment-email.hpp
@@ -0,0 +1,42 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2017-2022, Regents of the University of California.
+ *
+ * This file is part of ndncert, a certificate management system based on NDN.
+ *
+ * ndncert is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * ndncert is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License along with
+ * ndncert, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndncert authors and contributors.
+ */
+
+#ifndef NDNCERT_ASSIGNMENT_EMAIL_HPP
+#define NDNCERT_ASSIGNMENT_EMAIL_HPP
+
+#include "assignment-func.hpp"
+
+namespace ndncert {
+
+/**
+ * assign names base on client probe parameter
+ */
+class AssignmentEmail : public NameAssignmentFunc
+{
+public:
+  explicit AssignmentEmail(const std::string& format = "");
+
+  std::vector<ndn::PartialName>
+  assignName(const std::multimap<std::string, std::string>& params) override;
+};
+
+} // namespace ndncert
+
+#endif // NDNCERT_ASSIGNMENT_EMAIL_HPP
diff --git a/src/name-assignment/assignment-param.cpp b/src/name-assignment/assignment-param.cpp
index 956b3d8..e1961e7 100644
--- a/src/name-assignment/assignment-param.cpp
+++ b/src/name-assignment/assignment-param.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,13 +35,17 @@
   std::vector<ndn::PartialName> resultList;
   Name result;
   for (const auto& item : m_nameFormat) {
-    auto it = std::find_if(params.begin(), params.end(),
-                           [&](const std::tuple<std::string, std::string>& e) { return std::get<0>(e) == item; });
-    if (it != params.end() && !it->second.empty()) {
-      result.append(it->second);
+    if (item.size() >= 2 && item[0] == '"' && item[item.size() - 1] == '"') {
+      result.append(item.substr(1, item.size() - 2));
     }
     else {
-      return resultList;
+      auto it = params.find(item);
+      if (it != params.end() && !it->second.empty()) {
+        result.append(it->second);
+      }
+      else {
+        return resultList; // empty
+      }
     }
   }
   resultList.push_back(std::move(result));
diff --git a/src/redirection/redirection-email.cpp b/src/redirection/redirection-email.cpp
new file mode 100644
index 0000000..863eb91
--- /dev/null
+++ b/src/redirection/redirection-email.cpp
@@ -0,0 +1,46 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2017-2022, Regents of the University of California.
+ *
+ * This file is part of ndncert, a certificate management system based on NDN.
+ *
+ * ndncert is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * ndncert is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License along with
+ * ndncert, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndncert authors and contributors.
+ */
+
+#include "redirection-email.hpp"
+#include <boost/algorithm/string.hpp>
+
+namespace ndncert {
+
+NDNCERT_REGISTER_POLICY_FACTORY(RedirectionEmail, "email");
+
+RedirectionEmail::RedirectionEmail(const std::string& format)
+  : RedirectionPolicy(format)
+{
+  m_domain = format;
+}
+
+bool
+RedirectionEmail::isRedirecting(const std::multimap<std::string, std::string>& params)
+{
+  for (auto it = params.find("email"); it != params.end() && it->first == "email"; it++) {
+    auto i = it->second.rfind('@');
+    if (i != std::string::npos && it->second.substr(i + 1) == m_domain) {
+      return true;
+    }
+  }
+  return false;
+}
+
+} // namespace ndncert
diff --git a/src/redirection/redirection-email.hpp b/src/redirection/redirection-email.hpp
new file mode 100644
index 0000000..9a338ef
--- /dev/null
+++ b/src/redirection/redirection-email.hpp
@@ -0,0 +1,45 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2017-2022, Regents of the University of California.
+ *
+ * This file is part of ndncert, a certificate management system based on NDN.
+ *
+ * ndncert is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * ndncert is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License along with
+ * ndncert, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndncert authors and contributors.
+ */
+
+#ifndef NDNCERT_REDIRECTION_EMAIL_HPP
+#define NDNCERT_REDIRECTION_EMAIL_HPP
+
+#include "redirection-policy.hpp"
+
+namespace ndncert {
+
+/**
+ * assign names base on client probe parameter
+ */
+class RedirectionEmail : public RedirectionPolicy
+{
+public:
+  explicit RedirectionEmail(const std::string& format = "");
+
+  bool
+  isRedirecting(const std::multimap<std::string, std::string>& params) override;
+
+private:
+  std::string m_domain;
+};
+
+} // namespace ndncert
+
+#endif // NDNCERT_REDIRECTION_EMAIL_HPP
diff --git a/src/redirection/redirection-param.cpp b/src/redirection/redirection-param.cpp
new file mode 100644
index 0000000..d74d105
--- /dev/null
+++ b/src/redirection/redirection-param.cpp
@@ -0,0 +1,63 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2017-2022, Regents of the University of California.
+ *
+ * This file is part of ndncert, a certificate management system based on NDN.
+ *
+ * ndncert is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * ndncert is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License along with
+ * ndncert, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndncert authors and contributors.
+ */
+
+#include "redirection-param.hpp"
+#include <boost/algorithm/string.hpp>
+
+namespace ndncert {
+
+NDNCERT_REGISTER_POLICY_FACTORY(RedirectionParam, "param");
+
+RedirectionParam::RedirectionParam(const std::string& format)
+  : RedirectionPolicy(format)
+{
+  if (format.empty()) {
+    return;
+  }
+  std::vector<std::string> strs;
+  boost::split(strs,format,boost::is_any_of("&"));
+  for (const auto& s : strs) {
+    auto i = s.find('=');
+    if (i == std::string::npos) {
+      NDN_THROW(std::runtime_error("Redirection param format: no '=' in format piece"));
+    }
+    m_format.emplace(s.substr(0, i), s.substr(i + 1));
+  }
+}
+
+bool
+RedirectionParam::isRedirecting(const std::multimap<std::string, std::string>& params)
+{
+  for (const auto& p : m_format) {
+    bool found = false;
+    for (auto it = params.find(p.first); it != params.end() && it->first == p.first; it ++) {
+      if (it->second == p.second) {
+        found = true;
+        break;
+      }
+    }
+    if (!found) {
+      return false;
+    }
+  }
+  return true;
+}
+
+} // namespace ndncert
diff --git a/src/redirection/redirection-param.hpp b/src/redirection/redirection-param.hpp
new file mode 100644
index 0000000..d067a1f
--- /dev/null
+++ b/src/redirection/redirection-param.hpp
@@ -0,0 +1,45 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2017-2022, Regents of the University of California.
+ *
+ * This file is part of ndncert, a certificate management system based on NDN.
+ *
+ * ndncert is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * ndncert is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License along with
+ * ndncert, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndncert authors and contributors.
+ */
+
+#ifndef NDNCERT_REDIRECTION_PARAM_HPP
+#define NDNCERT_REDIRECTION_PARAM_HPP
+
+#include "redirection-policy.hpp"
+
+namespace ndncert {
+
+/**
+ * assign names base on client probe parameter
+ */
+class RedirectionParam : public RedirectionPolicy
+{
+public:
+  explicit RedirectionParam(const std::string& format = "");
+
+  bool
+  isRedirecting(const std::multimap<std::string, std::string>& params) override;
+
+private:
+  std::map<std::string, std::string> m_format;
+};
+
+} // namespace ndncert
+
+#endif // NDNCERT_REDIRECTION_PARAM_HPP
diff --git a/src/redirection/redirection-policy.cpp b/src/redirection/redirection-policy.cpp
new file mode 100644
index 0000000..88aef3e
--- /dev/null
+++ b/src/redirection/redirection-policy.cpp
@@ -0,0 +1,40 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2017-2022, Regents of the University of California.
+ *
+ * This file is part of ndncert, a certificate management system based on NDN.
+ *
+ * ndncert is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * ndncert is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License along with
+ * ndncert, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndncert authors and contributors.
+ */
+
+#include "redirection-policy.hpp"
+
+namespace ndncert {
+
+std::unique_ptr<RedirectionPolicy>
+RedirectionPolicy::createPolicyFunc(const std::string& policyType, const std::string& format)
+{
+  PolicyFactory& factory = getFactory();
+  auto i = factory.find(policyType);
+  return i == factory.end() ? nullptr : i->second(format);
+}
+
+RedirectionPolicy::PolicyFactory&
+RedirectionPolicy::getFactory()
+{
+  static PolicyFactory factory;
+  return factory;
+}
+
+} // namespace ndncert
diff --git a/src/redirection/redirection-policy.hpp b/src/redirection/redirection-policy.hpp
new file mode 100644
index 0000000..cba0cd8
--- /dev/null
+++ b/src/redirection/redirection-policy.hpp
@@ -0,0 +1,81 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2017-2022, Regents of the University of California.
+ *
+ * This file is part of ndncert, a certificate management system based on NDN.
+ *
+ * ndncert is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * ndncert is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License along with
+ * ndncert, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndncert authors and contributors.
+ */
+
+#ifndef NDNCERT_REDIRECTION_POLICY_HPP
+#define NDNCERT_REDIRECTION_POLICY_HPP
+
+#include "detail/ca-request-state.hpp"
+
+#include <map>
+
+namespace ndncert {
+
+class RedirectionPolicy : boost::noncopyable
+{
+protected:
+  explicit RedirectionPolicy(const std::string& format = "") {}
+
+public:
+  virtual ~RedirectionPolicy() = default;
+
+  /**
+   * @brief The Redirection Policy provided by the CA operator to decide if redirection is suitable.
+   *
+   *
+   * @param vector A list of parameter key-value pair from probe.
+   * @return a boolean that is true if the provided params conform to the configured redirection policy.
+   */
+  virtual bool
+  isRedirecting(const std::multimap<std::string, std::string>& params) = 0;
+
+public:
+  template <class PolicyType>
+  static void
+  registerRedirectionPolicy(const std::string& typeName)
+  {
+    PolicyFactory& factory = getFactory();
+    BOOST_ASSERT(factory.count(typeName) == 0);
+    factory[typeName] = [](const std::string& format) { return std::make_unique<PolicyType>(format); };
+  }
+
+  static std::unique_ptr<RedirectionPolicy>
+  createPolicyFunc(const std::string& policyType, const std::string& format = "");
+
+private:
+  typedef std::function<std::unique_ptr<RedirectionPolicy>(const std::string&)> FactoryCreateFunc;
+  typedef std::map<std::string, FactoryCreateFunc> PolicyFactory;
+
+  static PolicyFactory&
+  getFactory();
+};
+
+#define NDNCERT_REGISTER_POLICY_FACTORY(C, T)                                  \
+  static class NdnCert##C##PolicyFactoryRegistrationClass                      \
+  {                                                                               \
+  public:                                                                         \
+    NdnCert##C##PolicyFactoryRegistrationClass()                               \
+    {                                                                             \
+      ::ndncert::RedirectionPolicy::registerRedirectionPolicy<C>(T);        \
+    }                                                                             \
+  } g_NdnCert##C##RedirectionPolicyRegistrationVariable
+
+} // namespace ndncert
+
+#endif // NDNCERT_REDIRECTION_POLICY_HPP
diff --git a/src/requester-request.cpp b/src/requester-request.cpp
index 6ab56ea..5d9896f 100644
--- a/src/requester-request.cpp
+++ b/src/requester-request.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.
  *
diff --git a/systemd/ndncert-ca.service.in b/systemd/ndncert-ca.service.in
index 94ac006..97c81d8 100644
--- a/systemd/ndncert-ca.service.in
+++ b/systemd/ndncert-ca.service.in
@@ -1,14 +1,41 @@
 [Unit]
 Description=Certificate Management Identity Management Service for NDN
+BindsTo=nfd.service
+After=nfd.service
 
 [Service]
 Environment=HOME=%S/ndncert-ca
 ExecStart=@BINDIR@/ndncert-ca-server
 Restart=on-failure
 RestartPreventExitStatus=2
-RestartSec=5
 User=ndn
 
+CapabilityBoundingSet=
+LockPersonality=yes
+MemoryDenyWriteExecute=yes
+NoNewPrivileges=yes
+PrivateDevices=yes
+PrivateTmp=yes
+PrivateUsers=yes
+ProtectControlGroups=yes
+ProtectHome=yes
+ProtectKernelModules=yes
+ProtectKernelTunables=yes
+# systemd older than v232 doesn't support a value of "strict" for ProtectSystem,
+# so it will ignore that line and use ProtectSystem=full; with newer systemd,
+# the latter assignment is recognized and takes precedence, resulting in an
+# effective setting of ProtectSystem=strict
+ProtectSystem=full
+ProtectSystem=strict
+RestrictAddressFamilies=AF_UNIX AF_NETLINK AF_INET AF_INET6 AF_PACKET
+RestrictNamespaces=yes
+RestrictRealtime=yes
+StateDirectory=ndncert-ca
+SystemCallArchitectures=native
+SystemCallErrorNumber=EPERM
+SystemCallFilter=~@aio @chown @clock @cpu-emulation @debug @keyring @module @mount @obsolete @privileged @raw-io @reboot @resources @setuid @swap
+
+# Dependency
 [Install]
 WantedBy=multi-user.target
-Alias=ndncert.service
\ No newline at end of file
+WantedBy=nfd.service
\ No newline at end of file
diff --git a/tests/unit-tests/ca-module.t.cpp b/tests/unit-tests/ca-module.t.cpp
index d4a733d..2a9640b 100644
--- a/tests/unit-tests/ca-module.t.cpp
+++ b/tests/unit-tests/ca-module.t.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.
  *
@@ -211,8 +211,8 @@
       }
     }
     BOOST_CHECK_EQUAL(redirectionItems.size(), 2);
-    BOOST_CHECK_EQUAL(ndn::security::extractIdentityFromCertName(redirectionItems[0].getPrefix(-1)), "/ndn/site1");
-    BOOST_CHECK_EQUAL(ndn::security::extractIdentityFromCertName(redirectionItems[1].getPrefix(-1)), "/ndn/site1");
+    BOOST_CHECK_EQUAL(ndn::security::extractIdentityFromCertName(redirectionItems[0].getPrefix(-1)), "/ndn/edu/ucla");
+    BOOST_CHECK_EQUAL(ndn::security::extractIdentityFromCertName(redirectionItems[1].getPrefix(-1)), "/ndn/edu/ucla/cs/irl");
   });
   face.receive(interest);
   advanceClocks(time::milliseconds(20), 60);
diff --git a/tests/unit-tests/challenge-email.t.cpp b/tests/unit-tests/challenge-email.t.cpp
index 92fb45b..1044660 100644
--- a/tests/unit-tests/challenge-email.t.cpp
+++ b/tests/unit-tests/challenge-email.t.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.
  *
@@ -93,30 +93,6 @@
   std::remove("tmp.txt");
 }
 
-BOOST_AUTO_TEST_CASE(OnChallengeRequestWithInvalidEmail)
-{
-  auto identity = addIdentity(Name("/ndn/site1"));
-  auto key = identity.getDefaultKey();
-  auto cert = key.getDefaultCertificate();
-  RequestId requestId = {{101}};
-  ca::RequestState request;
-  request.caPrefix = Name("/ndn/site1");
-  request.requestId = requestId;
-  request.requestType = RequestType::NEW;
-  request.cert = cert;
-
-  Block paramTLV = ndn::makeEmptyBlock(tlv::EncryptedPayload);
-  paramTLV.push_back(ndn::makeStringBlock(tlv::ParameterKey, ChallengeEmail::PARAMETER_KEY_EMAIL));
-  paramTLV.push_back(ndn::makeStringBlock(tlv::ParameterValue, "zhiyi@cs"));
-
-  ChallengeEmail challenge;
-  challenge.handleChallengeRequest(paramTLV, request);
-
-  BOOST_CHECK_EQUAL(request.challengeType, "email");
-  BOOST_CHECK_EQUAL(request.challengeState->challengeStatus, ChallengeEmail::INVALID_EMAIL);
-  BOOST_CHECK_EQUAL(request.challengeState->remainingTries, 2);
-}
-
 BOOST_AUTO_TEST_CASE(OnChallengeRequestWithCode)
 {
   auto identity = addIdentity(Name("/ndn/site1"));
diff --git a/tests/unit-tests/config-files/config-ca-5 b/tests/unit-tests/config-files/config-ca-5
index f867534..5d306aa 100644
--- a/tests/unit-tests/config-files/config-ca-5
+++ b/tests/unit-tests/config-files/config-ca-5
@@ -15,11 +15,15 @@
   [
       {
         "ca-prefix": "/ndn/edu/ucla",
-        "certificate": "Bv0CJAcsCANuZG4IBXNpdGUxCANLRVkICBG8IvRjFf8XCARzZWxmCAn9AAABWcgU2aUUCRgBAhkEADbugBX9AU8wggFLMIIBAwYHKoZIzj0CATCB9wIBATAsBgcqhkjOPQEBAiEA/////wAAAAEAAAAAAAAAAAAAAAD///////////////8wWwQg/////wAAAAEAAAAAAAAAAAAAAAD///////////////wEIFrGNdiqOpPns+u9VXaYhrxlHQawzFOw9jvOPD4n0mBLAxUAxJ02CIbnBJNqZnjhE50mt4GffpAEQQRrF9Hy4SxCR/i85uVjpEDydwN9gS3rM6D0oTlF2JjClk/jQuL+Gn+bjufrSnwPnhYrzjNXazFezsu2QGg3v1H1AiEA/////wAAAAD//////////7zm+q2nF56E87nKwvxjJVECAQEDQgAES9Cb9iANUNYmwt5bjwNW1mZgjzIkDJb6FTCdiYWnkMMIVxh2YDllphoWDEAPS6kqJczzCuhnGYpZCp9tTaYKGxZMGwEDHB0HGwgDbmRuCAVzaXRlMQgDS0VZCAgRvCL0YxX/F/0A/Sb9AP4PMTk3MDAxMDFUMDAwMDAw/QD/DzIwMzcwMTE3VDIxMjg0NhdIMEYCIQDXkR1hF3GiP7yLXq+0JBJfi9QC+hhAu/1Bykx+MWz6RAIhANwelBTxxZr2C5bD15mjfhWudK4I1tOb4b/9xWCHyM7F"
+        "certificate": "Bv0BNQcvCANuZG4IA2VkdQgEdWNsYQgDS0VZCAhtCJjCeE5aEwgEc2VsZjYIAAABf1ePw8kUCRgBAhkEADbugBVbMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEwrhu4gD0eba7jbVqg1qfwuRj2JSqOfnwdiOlholvhCGpdXHpqg5o68ajADQPL9S555uvfabbFnPhv86X/Diy5hZQGwEDHCEHHwgDbmRuCANlZHUIBHVjbGEIA0tFWQgIbQiYwnhOWhP9AP0m/QD+DzE5NzAwMTAxVDAwMDAwMP0A/w8yMDQyMDIyOFQwMDUxNTMXSDBGAiEA11i8sGwf83hd+IQ2vve+Ax1O7zZeV8cG6FAXvXFQ0kACIQDAvqq0CRAYYJ/RFLW21wNGJf1Rf3OgFyGEKpLjnRkxaw==",
+        "policy-type": "param",
+        "policy-param": ""
       },
       {
-        "ca-prefix": "/ndn/edu/irl",
-        "certificate": "Bv0CJAcsCANuZG4IBXNpdGUxCANLRVkICBG8IvRjFf8XCARzZWxmCAn9AAABWcgU2aUUCRgBAhkEADbugBX9AU8wggFLMIIBAwYHKoZIzj0CATCB9wIBATAsBgcqhkjOPQEBAiEA/////wAAAAEAAAAAAAAAAAAAAAD///////////////8wWwQg/////wAAAAEAAAAAAAAAAAAAAAD///////////////wEIFrGNdiqOpPns+u9VXaYhrxlHQawzFOw9jvOPD4n0mBLAxUAxJ02CIbnBJNqZnjhE50mt4GffpAEQQRrF9Hy4SxCR/i85uVjpEDydwN9gS3rM6D0oTlF2JjClk/jQuL+Gn+bjufrSnwPnhYrzjNXazFezsu2QGg3v1H1AiEA/////wAAAAD//////////7zm+q2nF56E87nKwvxjJVECAQEDQgAES9Cb9iANUNYmwt5bjwNW1mZgjzIkDJb6FTCdiYWnkMMIVxh2YDllphoWDEAPS6kqJczzCuhnGYpZCp9tTaYKGxZMGwEDHB0HGwgDbmRuCAVzaXRlMQgDS0VZCAgRvCL0YxX/F/0A/Sb9AP4PMTk3MDAxMDFUMDAwMDAw/QD/DzIwMzcwMTE3VDIxMjg0NhdIMEYCIQDXkR1hF3GiP7yLXq+0JBJfi9QC+hhAu/1Bykx+MWz6RAIhANwelBTxxZr2C5bD15mjfhWudK4I1tOb4b/9xWCHyM7F"
+        "ca-prefix": "/ndn/edu/ucla/cs/irl",
+        "certificate": "Bv0BRQc4CANuZG4IA2VkdQgEdWNsYQgCY3MIA2lybAgDS0VZCAgWVGa5Tzd9WggEc2VsZjYIAAABf1eQ1LoUCRgBAhkEADbugBVbMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE7hBKT9GapKRII3LT9q0kY5RXEE9Cu9B2Pg/E4Mndbqr3nbnMmm+SUAeIcrnTQa4c9ri8oCLkTesXsW0Tr8oTuhZZGwEDHCoHKAgDbmRuCANlZHUIBHVjbGEIAmNzCANpcmwIA0tFWQgIFlRmuU83fVr9AP0m/QD+DzE5NzAwMTAxVDAwMDAwMP0A/w8yMDQyMDIyOFQwMDUzMDMXRjBEAiA9Q/FjffFLasMfr7MIQY/KBBQScNKYyrEyphz4wOcQjAIgLf14XL8LaqqUyfBkwQXeCv3pipsnZw5BFhv8c5UCLVE=",
+        "policy-type": "param",
+        "policy-param": ""
       }
   ],
   "name-assignment":
diff --git a/tests/unit-tests/config-files/config-client-1 b/tests/unit-tests/config-files/config-client-1
index 20daad1..79d9519 100644
--- a/tests/unit-tests/config-files/config-client-1
+++ b/tests/unit-tests/config-files/config-client-1
@@ -10,11 +10,11 @@
         [
           { "probe-parameter-key": "email" }
         ],
-        "certificate": "Bv0CJAcsCANuZG4IBXNpdGUxCANLRVkICBG8IvRjFf8XCARzZWxmCAn9AAABWcgU2aUUCRgBAhkEADbugBX9AU8wggFLMIIBAwYHKoZIzj0CATCB9wIBATAsBgcqhkjOPQEBAiEA/////wAAAAEAAAAAAAAAAAAAAAD///////////////8wWwQg/////wAAAAEAAAAAAAAAAAAAAAD///////////////wEIFrGNdiqOpPns+u9VXaYhrxlHQawzFOw9jvOPD4n0mBLAxUAxJ02CIbnBJNqZnjhE50mt4GffpAEQQRrF9Hy4SxCR/i85uVjpEDydwN9gS3rM6D0oTlF2JjClk/jQuL+Gn+bjufrSnwPnhYrzjNXazFezsu2QGg3v1H1AiEA/////wAAAAD//////////7zm+q2nF56E87nKwvxjJVECAQEDQgAES9Cb9iANUNYmwt5bjwNW1mZgjzIkDJb6FTCdiYWnkMMIVxh2YDllphoWDEAPS6kqJczzCuhnGYpZCp9tTaYKGxZMGwEDHB0HGwgDbmRuCAVzaXRlMQgDS0VZCAgRvCL0YxX/F/0A/Sb9AP4PMTk3MDAxMDFUMDAwMDAw/QD/DzIwMzcwMTE3VDIxMjg0NhdIMEYCIQDXkR1hF3GiP7yLXq+0JBJfi9QC+hhAu/1Bykx+MWz6RAIhANwelBTxxZr2C5bD15mjfhWudK4I1tOb4b/9xWCHyM7F"
+        "certificate": "Bv0BKwcrCANuZG4IBXNpdGUxCANLRVkICEKyYEYHiBwyCARzZWxmNggAAAF/V5V9QhQJGAECGQQANu6AFVswWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAARRaZbFLRSnL1fmj8X3hInCCPy4qe17QujMMYq8qe+CKqV+OjexhrUvpwRDImRZOgXLCjOTyYnW3wxlxskTz8Y3FkwbAQMcHQcbCANuZG4IBXNpdGUxCANLRVkICEKyYEYHiBwy/QD9Jv0A/g8xOTcwMDEwMVQwMDAwMDD9AP8PMjA0MjAyMjhUMDA1ODA5F0YwRAIgFtFP0WocLQCtbwMTnqNtnCDmu62EJyC4uuCZ4Q/Wb8UCIGHb3e4St78378py81GjEZd/2L/aGbE3vbYQIiNxIYPN"
     },
     {
         "ca-prefix": "/ndn/edu/ucla/zhiyi",
-        "certificate": "Bv0CJAcsCANuZG4IBXNpdGUxCANLRVkICBG8IvRjFf8XCARzZWxmCAn9AAABWcgU2aUUCRgBAhkEADbugBX9AU8wggFLMIIBAwYHKoZIzj0CATCB9wIBATAsBgcqhkjOPQEBAiEA/////wAAAAEAAAAAAAAAAAAAAAD///////////////8wWwQg/////wAAAAEAAAAAAAAAAAAAAAD///////////////wEIFrGNdiqOpPns+u9VXaYhrxlHQawzFOw9jvOPD4n0mBLAxUAxJ02CIbnBJNqZnjhE50mt4GffpAEQQRrF9Hy4SxCR/i85uVjpEDydwN9gS3rM6D0oTlF2JjClk/jQuL+Gn+bjufrSnwPnhYrzjNXazFezsu2QGg3v1H1AiEA/////wAAAAD//////////7zm+q2nF56E87nKwvxjJVECAQEDQgAES9Cb9iANUNYmwt5bjwNW1mZgjzIkDJb6FTCdiYWnkMMIVxh2YDllphoWDEAPS6kqJczzCuhnGYpZCp9tTaYKGxZMGwEDHB0HGwgDbmRuCAVzaXRlMQgDS0VZCAgRvCL0YxX/F/0A/Sb9AP4PMTk3MDAxMDFUMDAwMDAw/QD/DzIwMzcwMTE3VDIxMjg0NhdIMEYCIQDXkR1hF3GiP7yLXq+0JBJfi9QC+hhAu/1Bykx+MWz6RAIhANwelBTxxZr2C5bD15mjfhWudK4I1tOb4b/9xWCHyM7F"
+        "certificate": "Bv0BKwcrCANuZG4IBXNpdGUxCANLRVkICEKyYEYHiBwyCARzZWxmNggAAAF/V5V9QhQJGAECGQQANu6AFVswWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAARRaZbFLRSnL1fmj8X3hInCCPy4qe17QujMMYq8qe+CKqV+OjexhrUvpwRDImRZOgXLCjOTyYnW3wxlxskTz8Y3FkwbAQMcHQcbCANuZG4IBXNpdGUxCANLRVkICEKyYEYHiBwy/QD9Jv0A/g8xOTcwMDEwMVQwMDAwMDD9AP8PMjA0MjAyMjhUMDA1ODA5F0YwRAIgFtFP0WocLQCtbwMTnqNtnCDmu62EJyC4uuCZ4Q/Wb8UCIGHb3e4St78378py81GjEZd/2L/aGbE3vbYQIiNxIYPN"
     }
   ]
 }
\ No newline at end of file
diff --git a/tests/unit-tests/configuration.t.cpp b/tests/unit-tests/configuration.t.cpp
index f7a8bc5..bb380f9 100644
--- a/tests/unit-tests/configuration.t.cpp
+++ b/tests/unit-tests/configuration.t.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,8 +51,8 @@
   BOOST_CHECK_EQUAL(config.caProfile.supportedChallenges.front(), "pin");
 
   config.load("tests/unit-tests/config-files/config-ca-5");
-  BOOST_CHECK_EQUAL(config.redirection[0]->getName(),
-                    "/ndn/site1/KEY/%11%BC%22%F4c%15%FF%17/self/%FD%00%00%01Y%C8%14%D9%A5");
+  BOOST_CHECK_EQUAL(config.redirection[0].first->getName(),
+                    "/ndn/edu/ucla/KEY/m%08%98%C2xNZ%13/self/v=1646441513929");
   BOOST_CHECK_EQUAL(config.nameAssignmentFuncs.size(), 3);
   BOOST_CHECK_EQUAL(config.nameAssignmentFuncs[0]->m_nameFormat[0], "group");
   BOOST_CHECK_EQUAL(config.nameAssignmentFuncs[0]->m_nameFormat[1], "email");
@@ -99,7 +99,7 @@
   BOOST_CHECK_EQUAL(profile1.probeParameterKeys.size(), 1);
   BOOST_CHECK_EQUAL(profile1.probeParameterKeys.front(), "email");
   BOOST_CHECK_EQUAL(profile1.cert->getName(),
-                    "/ndn/site1/KEY/%11%BC%22%F4c%15%FF%17/self/%FD%00%00%01Y%C8%14%D9%A5");
+                    "/ndn/site1/KEY/B%B2%60F%07%88%1C2/self/v=1646441889090");
 
   auto& profile2 = profileStorage.getKnownProfiles().back();
   BOOST_CHECK_EQUAL(profile2.caPrefix, "/ndn/edu/ucla/zhiyi");
@@ -108,7 +108,7 @@
   BOOST_CHECK(!profile2.maxSuffixLength);
   BOOST_CHECK_EQUAL(profile2.probeParameterKeys.size(), 0);
   BOOST_CHECK_EQUAL(profile2.cert->getName(),
-                    "/ndn/site1/KEY/%11%BC%22%F4c%15%FF%17/self/%FD%00%00%01Y%C8%14%D9%A5");
+                    "/ndn/site1/KEY/B%B2%60F%07%88%1C2/self/v=1646441889090");
 }
 
 BOOST_AUTO_TEST_CASE(ProfileStorageWithErrors)
diff --git a/tests/unit-tests/name-assignment.t.cpp b/tests/unit-tests/name-assignment.t.cpp
index 94b7fef..cecbc59 100644
--- a/tests/unit-tests/name-assignment.t.cpp
+++ b/tests/unit-tests/name-assignment.t.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.
  *
@@ -18,6 +18,7 @@
  * See AUTHORS.md for complete list of ndncert authors and contributors.
  */
 
+#include "name-assignment/assignment-email.hpp"
 #include "name-assignment/assignment-random.hpp"
 #include "name-assignment/assignment-param.hpp"
 #include "name-assignment/assignment-hash.hpp"
@@ -49,6 +50,14 @@
   BOOST_CHECK_EQUAL(*assignment.assignName(params).begin(), Name("/123/789"));
   params.find("xyz")->second = "";
   BOOST_CHECK_EQUAL(assignment.assignName(params).size(), 0);
+
+  AssignmentParam assignment2("/\"guest\"/email");
+  params.emplace("email", "1@1.com");
+  BOOST_CHECK_EQUAL(assignment2.assignName(params).size(), 1);
+  BOOST_CHECK_EQUAL(assignment2.assignName(params).begin()->toUri(), Name("/guest/1@1.com").toUri());
+
+  AssignmentParam assignment3("/\"/email");
+  BOOST_CHECK_EQUAL(assignment3.assignName(params).size(), 0);
 }
 
 BOOST_AUTO_TEST_CASE(NameAssignmentHash)
@@ -68,6 +77,20 @@
   BOOST_CHECK_EQUAL(assignment.assignName(params).begin()->size(), 2);
 }
 
+BOOST_AUTO_TEST_CASE(NameAssignmentEmail)
+{
+  AssignmentEmail assignment("/edu/ucla");
+  std::multimap<std::string, std::string> params;
+  BOOST_CHECK_EQUAL(assignment.assignName(params).size(), 0);
+  params.emplace("email", "das@math.ucla.edu");
+  BOOST_CHECK_EQUAL(*assignment.assignName(params).begin(), Name("/math/das"));
+
+  params.clear();
+  params.emplace("email", "d/~.^as@6666=.9!");
+  BOOST_CHECK_EQUAL(assignment.assignName(params).size(), 1);
+  BOOST_CHECK_EQUAL(*assignment.assignName(params).begin(), Name("/9!/6666%3D/d%2F~.%5Eas"));
+}
+
 BOOST_AUTO_TEST_SUITE_END() // TestNameAssignment
 
 } // namespace tests
diff --git a/tests/unit-tests/protocol-encoders.t.cpp b/tests/unit-tests/protocol-encoders.t.cpp
index bc1daa0..720c10e 100644
--- a/tests/unit-tests/protocol-encoders.t.cpp
+++ b/tests/unit-tests/protocol-encoders.t.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.
  *
@@ -79,7 +79,9 @@
   std::vector<Name> names;
   names.emplace_back("/ndn/1");
   names.emplace_back("/ndn/2");
-  auto b = probetlv::encodeDataContent(names, 2, config.redirection);
+  std::vector<Name> redirectionNames;
+  for (const auto& i : config.redirection) redirectionNames.push_back(i.first->getFullName());
+  auto b = probetlv::encodeDataContent(names, 2, redirectionNames);
   std::vector<std::pair<Name, int>> retNames;
   std::vector<Name> redirection;
   probetlv::decodeDataContent(b, retNames, redirection);
@@ -94,7 +96,7 @@
   auto it3 = redirection.begin();
   auto it4 = config.redirection.begin();
   for (; it3 != redirection.end() && it4 != config.redirection.end(); it3++, it4++) {
-    BOOST_CHECK_EQUAL(*it3, (*it4)->getFullName());
+    BOOST_CHECK_EQUAL(*it3, it4->first->getFullName());
   }
 }
 
diff --git a/tests/unit-tests/redirection-policy.t.cpp b/tests/unit-tests/redirection-policy.t.cpp
new file mode 100644
index 0000000..d8d5203
--- /dev/null
+++ b/tests/unit-tests/redirection-policy.t.cpp
@@ -0,0 +1,80 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2017-2022, Regents of the University of California.
+ *
+ * This file is part of ndncert, a certificate management system based on NDN.
+ *
+ * ndncert is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * ndncert is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License along with
+ * ndncert, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndncert authors and contributors.
+ */
+
+#include "redirection/redirection-policy.hpp"
+#include "redirection/redirection-param.hpp"
+#include "redirection/redirection-email.hpp"
+#include "test-common.hpp"
+
+namespace ndncert {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(TestRedirectionPolicy)
+
+BOOST_AUTO_TEST_CASE(RedirectionPolicyParam)
+{
+  RedirectionParam assignment("");
+  std::multimap<std::string, std::string> params;
+  BOOST_CHECK(assignment.isRedirecting(params));
+  params.emplace("abc", "123");
+  BOOST_CHECK(assignment.isRedirecting(params));
+
+  RedirectionParam assignment1("abc=123");
+  params.clear();
+  BOOST_CHECK(!assignment1.isRedirecting(params));
+  params.emplace("abc", "124");
+  BOOST_CHECK(!assignment1.isRedirecting(params));
+  params.emplace("abc", "123");
+  BOOST_CHECK(assignment1.isRedirecting(params));
+
+  RedirectionParam assignment2("abc=123&xyz=789");
+  params.clear();
+  BOOST_CHECK(!assignment2.isRedirecting(params));
+  params.emplace("abc", "123");
+  BOOST_CHECK(!assignment2.isRedirecting(params));
+  params.emplace("xyz", "788");
+  BOOST_CHECK(!assignment2.isRedirecting(params));
+  params.emplace("xyz", "789");
+  BOOST_CHECK(assignment2.isRedirecting(params));
+  params.emplace("abz", "789");
+  BOOST_CHECK(assignment2.isRedirecting(params));
+}
+
+BOOST_AUTO_TEST_CASE(RedirectionPolicyEmail)
+{
+  RedirectionEmail assignment("cs.ucla.edu");
+  std::multimap<std::string, std::string> params;
+  BOOST_CHECK(!assignment.isRedirecting(params));
+  params.emplace("email", "das@math.ucla.edu");
+  BOOST_CHECK(!assignment.isRedirecting(params));
+
+  params.clear();
+  params.emplace("email", "das@cs.ucla.edu");
+  BOOST_CHECK(assignment.isRedirecting(params));
+
+  params.clear();
+  params.emplace("email", "das@ucla.edu");
+  BOOST_CHECK(!assignment.isRedirecting(params));
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestNameAssignment
+
+} // namespace tests
+} // namespace ndncert
diff --git a/tests/unit-tests/requester.t.cpp b/tests/unit-tests/requester.t.cpp
index d2833c4..73da390 100644
--- a/tests/unit-tests/requester.t.cpp
+++ b/tests/unit-tests/requester.t.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.
  *
@@ -70,8 +70,8 @@
   ca_profile.cert = std::make_shared<Certificate>(cert);
 
   std::vector<Name> availableNames;
-  availableNames.push_back(Name("/site1"));
-  availableNames.push_back(Name("/site2"));
+  availableNames.emplace_back("/site1");
+  availableNames.emplace_back("/site2");
 
   ndn::util::DummyClientFace face(io, m_keyChain, {true, true});
   ca::CaModule ca(face, m_keyChain, "tests/unit-tests/config-files/config-ca-5", "ca-storage-memory");
@@ -79,7 +79,11 @@
   Data reply;
   reply.setName(Name("/site/CA/PROBE"));
   reply.setFreshnessPeriod(time::seconds(100));
-  reply.setContent(probetlv::encodeDataContent(availableNames, 3, ca.m_config.redirection));
+  {
+    std::vector<Name> redirectionNames;
+    for (const auto &i : ca.m_config.redirection) redirectionNames.push_back(i.first->getFullName());
+    reply.setContent(probetlv::encodeDataContent(availableNames, 3, redirectionNames));
+  }
   m_keyChain.sign(reply, ndn::signingByIdentity(identity));
 
   std::vector<std::pair<Name, int>> names;
@@ -94,8 +98,8 @@
   BOOST_CHECK_EQUAL(names[1].second, 3);
 
   BOOST_CHECK_EQUAL(redirects.size(), 2);
-  BOOST_CHECK_EQUAL(ndn::security::extractIdentityFromCertName(redirects[0].getPrefix(-1)), "/ndn/site1");
-  BOOST_CHECK_EQUAL(ndn::security::extractIdentityFromCertName(redirects[1].getPrefix(-1)), "/ndn/site1");
+  BOOST_CHECK_EQUAL(ndn::security::extractIdentityFromCertName(redirects[0].getPrefix(-1)), "/ndn/edu/ucla");
+  BOOST_CHECK_EQUAL(ndn::security::extractIdentityFromCertName(redirects[1].getPrefix(-1)), "/ndn/edu/ucla/cs/irl");
 }
 
 BOOST_AUTO_TEST_CASE(ErrorHandling)
diff --git a/tools/ndncert-ca-server.cpp b/tools/ndncert-ca-server.cpp
index 5f0e3a3..8260502 100644
--- a/tools/ndncert-ca-server.cpp
+++ b/tools/ndncert-ca-server.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.
  *
@@ -155,9 +155,6 @@
               return;
             }
           }
-        },
-        [](const Name&, const std::string& errorInfo) {
-          std::cerr << "ERROR: " << errorInfo << std::endl;
         });
   }