Probe: add probe token to New and Challenge
Change-Id: Iae8b009bb2e78f03910e53fb49e750ebc8a6e6ae
diff --git a/AUTHORS.md b/AUTHORS.md
index efe9a10..e376112 100644
--- a/AUTHORS.md
+++ b/AUTHORS.md
@@ -4,7 +4,7 @@
## The primary authors are (and/or have been):
* Zhiyi Zhang <https://zhiyi-zhang.com>
-* Yufeng Zhang <yufeng@ucla.edu>
+* Yufeng Zhang <yufeng@ucla.edu>
* Alexander Afanasyev <http://lasr.cs.ucla.edu/afanasyev/index.html>
* Lixia Zhang <http://web.cs.ucla.edu/~lixia/>
diff --git a/ca.conf.sample b/ca.conf.sample
index 1a7db2e..d81a8e9 100644
--- a/ca.conf.sample
+++ b/ca.conf.sample
@@ -7,7 +7,7 @@
"validity-period": "360",
"ca-info": "NDN Testbed CA",
- "probe": "Use the university/organization name as input",
+ "probe": "email",
"targeted-list": "Use your email address (edu preferred) as input",
"related-ca-list":
diff --git a/client.conf.sample b/client.conf.sample
index e2079d3..a0de72a 100644
--- a/client.conf.sample
+++ b/client.conf.sample
@@ -4,7 +4,7 @@
{
"ca-prefix": "/ndn/CA",
"ca-info": "NDN Testbed CA",
- "probe": "Use the university/organization name as input",
+ "probe": "email",
"target-list": "Use your email address (edu preferred) as input",
"certificate": "Bv0CJAcsCANuZG4IBXNpdGUxCANLRVkICBG8IvRjFf8XCARzZWxmCAn9AAABWcgU2aUUCRgBAhkEADbugBX9AU8wggFLMIIBAwYHKoZIzj0CATCB9wIBATAsBgcqhkjOPQEBAiEA/////wAAAAEAAAAAAAAAAAAAAAD///////////////8wWwQg/////wAAAAEAAAAAAAAAAAAAAAD///////////////wEIFrGNdiqOpPns+u9VXaYhrxlHQawzFOw9jvOPD4n0mBLAxUAxJ02CIbnBJNqZnjhE50mt4GffpAEQQRrF9Hy4SxCR/i85uVjpEDydwN9gS3rM6D0oTlF2JjClk/jQuL+Gn+bjufrSnwPnhYrzjNXazFezsu2QGg3v1H1AiEA/////wAAAAD//////////7zm+q2nF56E87nKwvxjJVECAQEDQgAES9Cb9iANUNYmwt5bjwNW1mZgjzIkDJb6FTCdiYWnkMMIVxh2YDllphoWDEAPS6kqJczzCuhnGYpZCp9tTaYKGxZMGwEDHB0HGwgDbmRuCAVzaXRlMQgDS0VZCAgRvCL0YxX/F/0A/Sb9AP4PMTk3MDAxMDFUMDAwMDAw/QD/DzIwMzcwMTE3VDIxMjg0NhdIMEYCIQDXkR1hF3GiP7yLXq+0JBJfi9QC+hhAu/1Bykx+MWz6RAIhANwelBTxxZr2C5bD15mjfhWudK4I1tOb4b/9xWCHyM7F"
}
diff --git a/src/ca-detail/ca-sqlite.cpp b/src/ca-detail/ca-sqlite.cpp
index bb88cc7..cf53022 100644
--- a/src/ca-detail/ca-sqlite.cpp
+++ b/src/ca-detail/ca-sqlite.cpp
@@ -48,7 +48,8 @@
challenge_secrets TEXT,
challenge_tp TEXT,
remaining_tries INTEGER,
- remaining_time INTEGER
+ remaining_time INTEGER,
+ probe_token BLOB
);
CREATE UNIQUE INDEX IF NOT EXISTS
CertRequestIdIndex ON CertRequests(request_id);
@@ -129,9 +130,14 @@
std::string challengeTp = statement.getString(9);
int remainingTries = statement.getInt(10);
int remainingTime = statement.getInt(11);
- return CertificateRequest(caName, requestId, status, challengeStatus, challengeType,
+ CertificateRequest request(caName, requestId, status, challengeStatus, challengeType,
challengeTp, remainingTime, remainingTries,
convertString2Json(challengeSecrets), cert);
+ if (statement.getSize(12) != 0) {
+ shared_ptr<Data> probeToken = make_shared<Data>(statement.getBlock(12));
+ request.setProbeToken(probeToken);
+ }
+ return request;
}
else {
BOOST_THROW_EXCEPTION(Error("Request " + requestId + " cannot be fetched from database"));
@@ -141,6 +147,7 @@
void
CaSqlite::addRequest(const CertificateRequest& request)
{
+ // check whether request is there already
Sqlite3Statement statement1(m_database,
R"_SQLTEXT_(SELECT * FROM CertRequests where cert_key_name = ?)_SQLTEXT_");
statement1.bind(1, request.m_cert.getKeyName().wireEncode(), SQLITE_TRANSIENT);
@@ -149,6 +156,7 @@
return;
}
+ // check whether certificate is already issued
Sqlite3Statement statement2(m_database,
R"_SQLTEXT_(SELECT * FROM IssuedCerts where cert_key_name = ?)_SQLTEXT_");
statement2.bind(1, request.m_cert.getKeyName().wireEncode(), SQLITE_TRANSIENT);
@@ -157,25 +165,54 @@
return;
}
- Sqlite3Statement statement(m_database,
- R"_SQLTEXT_(INSERT INTO CertRequests (request_id, ca_name, status,
+ if (request.m_probeToken != nullptr) {
+ Sqlite3Statement statement(
+ m_database,
+ R"_SQLTEXT_(INSERT INTO CertRequests (request_id, ca_name, status,
+ challenge_status, cert_key_name, cert_request, challenge_type, challenge_secrets,
+ challenge_tp, remaining_tries, remaining_time, probe_token)
+ values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?))_SQLTEXT_");
+ statement.bind(1, request.m_requestId, SQLITE_TRANSIENT);
+ statement.bind(2, request.m_caName.wireEncode(), SQLITE_TRANSIENT);
+ statement.bind(3, request.m_status);
+ statement.bind(4, request.m_challengeStatus, SQLITE_TRANSIENT);
+ statement.bind(5, request.m_cert.getKeyName().wireEncode(),
+ SQLITE_TRANSIENT);
+ statement.bind(6, request.m_cert.wireEncode(), SQLITE_TRANSIENT);
+ statement.bind(7, request.m_challengeType, SQLITE_TRANSIENT);
+ statement.bind(8, convertJson2String(request.m_challengeSecrets),
+ SQLITE_TRANSIENT);
+ statement.bind(9, request.m_challengeTp, SQLITE_TRANSIENT);
+ statement.bind(10, request.m_remainingTries);
+ statement.bind(11, request.m_remainingTime);
+ statement.bind(12, request.m_probeToken->wireEncode(), SQLITE_TRANSIENT);
+ if (statement.step() != SQLITE_DONE) {
+ BOOST_THROW_EXCEPTION(Error("Request " + request.m_requestId + " cannot be added to database"));
+ }
+ }
+ else {
+ Sqlite3Statement statement(
+ m_database,
+ R"_SQLTEXT_(INSERT INTO CertRequests (request_id, ca_name, status,
challenge_status, cert_key_name, cert_request, challenge_type, challenge_secrets,
challenge_tp, remaining_tries, remaining_time)
values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?))_SQLTEXT_");
- statement.bind(1, request.m_requestId, SQLITE_TRANSIENT);
- statement.bind(2, request.m_caName.wireEncode(), SQLITE_TRANSIENT);
- statement.bind(3, request.m_status);
- statement.bind(4, request.m_challengeStatus, SQLITE_TRANSIENT);
- statement.bind(5, request.m_cert.getKeyName().wireEncode(), SQLITE_TRANSIENT);
- statement.bind(6, request.m_cert.wireEncode(), SQLITE_TRANSIENT);
- statement.bind(7, request.m_challengeType, SQLITE_TRANSIENT);
- statement.bind(8, convertJson2String(request.m_challengeSecrets), SQLITE_TRANSIENT);
- statement.bind(9, request.m_challengeTp, SQLITE_TRANSIENT);
- statement.bind(10, request.m_remainingTries);
- statement.bind(11, request.m_remainingTime);
-
- if (statement.step() != SQLITE_DONE) {
- BOOST_THROW_EXCEPTION(Error("Request " + request.m_requestId + " cannot be added to database"));
+ statement.bind(1, request.m_requestId, SQLITE_TRANSIENT);
+ statement.bind(2, request.m_caName.wireEncode(), SQLITE_TRANSIENT);
+ statement.bind(3, request.m_status);
+ statement.bind(4, request.m_challengeStatus, SQLITE_TRANSIENT);
+ statement.bind(5, request.m_cert.getKeyName().wireEncode(),
+ SQLITE_TRANSIENT);
+ statement.bind(6, request.m_cert.wireEncode(), SQLITE_TRANSIENT);
+ statement.bind(7, request.m_challengeType, SQLITE_TRANSIENT);
+ statement.bind(8, convertJson2String(request.m_challengeSecrets),
+ SQLITE_TRANSIENT);
+ statement.bind(9, request.m_challengeTp, SQLITE_TRANSIENT);
+ statement.bind(10, request.m_remainingTries);
+ statement.bind(11, request.m_remainingTime);
+ if (statement.step() != SQLITE_DONE) {
+ BOOST_THROW_EXCEPTION(Error("Request " + request.m_requestId + " cannot be added to database"));
+ }
}
}
diff --git a/src/ca-module.cpp b/src/ca-module.cpp
index b91a03c..1794528 100644
--- a/src/ca-module.cpp
+++ b/src/ca-module.cpp
@@ -189,7 +189,29 @@
return;
}
- // verify the self-signed certificate and the request
+ // parse probe token if any
+ std::string probeTokenStr = parameterJson.get("probe-token", "");
+ shared_ptr<Data> probeToken = nullptr;
+ if (probeTokenStr != "") {
+ try {
+ std::stringstream ss(probeTokenStr);
+ probeToken = io::load<security::v2::Certificate>(ss);
+ }
+ catch (const std::exception& e) {
+ _LOG_ERROR("Unrecognized probe token " << e.what());
+ return;
+ }
+ }
+ if (probeToken != nullptr) {
+ Name prefix = m_config.m_caName;
+ prefix.append("CA").append("_PROBE");
+ if (!prefix.isPrefixOf(probeToken->getName())) {
+ // the carried probe token is not a Probe Data packet
+ return;
+ }
+ }
+
+ // verify the self-signed certificate, the request, and the token
if (!m_config.m_caName.isPrefixOf(clientCert->getName()) // under ca prefix
|| !security::v2::Certificate::isValidName(clientCert->getName()) // is valid cert name
|| clientCert->getName().size() != m_config.m_caName.size() + IS_SUBNAME_MIN_OFFSET) {
@@ -204,15 +226,27 @@
_LOG_TRACE("Interest with bad signature.");
return;
}
+ if (probeToken != nullptr) {
+ const auto& pib = m_keyChain.getPib();
+ const auto& key = pib.getIdentity(m_config.m_caName).getDefaultKey();
+ const auto& caCert = key.getDefaultCertificate();
+ if (!security::verifySignature(*probeToken, caCert)) {
+ _LOG_TRACE("Token with bad signature.");
+ return;
+ }
+ }
// create new request instance
std::string requestId = std::to_string(random::generateWord64());
CertificateRequest certRequest(m_config.m_caName, requestId, STATUS_BEFORE_CHALLENGE, *clientCert);
+ if (probeToken != nullptr) {
+ certRequest.setProbeToken(probeToken);
+ }
try {
m_storage->addRequest(certRequest);
}
catch (const std::exception& e) {
- _LOG_TRACE("Cannot add new request instance into the storage" << e.what());
+ _LOG_TRACE("Cannot add new request instance into the storage " << e.what());
return;
}
@@ -390,7 +424,7 @@
CaModule::getCertificateRequest(const Interest& request)
{
std::string requestId = readString(request.getName().at(m_config.m_caName.size() + 2));
- _LOG_TRACE("Requet Id to query the database " << requestId);
+ _LOG_TRACE("Request Id to query the database " << requestId);
CertificateRequest certRequest;
try {
certRequest = m_storage->getRequest(requestId);
diff --git a/src/ca-module.hpp b/src/ca-module.hpp
index 9f40c24..3926d97 100644
--- a/src/ca-module.hpp
+++ b/src/ca-module.hpp
@@ -64,6 +64,12 @@
bool
setStatusUpdateCallback(const StatusUpdateCallback& onUpdateCallback);
+ static JsonSection
+ jsonFromBlock(const Block& block);
+
+ static Block
+ dataContentFromJson(const JsonSection& jsonSection);
+
PUBLIC_WITH_TESTS_ELSE_PRIVATE:
void
onProbe(const Interest& request);
@@ -86,15 +92,9 @@
security::v2::Certificate
issueCertificate(const CertificateRequest& certRequest);
- static Block
- dataContentFromJson(const JsonSection& jsonSection);
-
void
registerPrefix();
- static JsonSection
- jsonFromBlock(const Block& block);
-
PUBLIC_WITH_TESTS_ELSE_PRIVATE:
const JsonSection
genProbeResponseJson(const Name& identifier,
diff --git a/src/certificate-request.cpp b/src/certificate-request.cpp
index 8db1f6f..22b67b0 100644
--- a/src/certificate-request.cpp
+++ b/src/certificate-request.cpp
@@ -52,6 +52,12 @@
{
}
+void
+CertificateRequest::setProbeToken(const shared_ptr<Data>& probeToken)
+{
+ m_probeToken = probeToken;
+}
+
std::ostream&
operator<<(std::ostream& os, const CertificateRequest& request)
{
diff --git a/src/certificate-request.hpp b/src/certificate-request.hpp
index 8fa5a0d..96c70ca 100644
--- a/src/certificate-request.hpp
+++ b/src/certificate-request.hpp
@@ -45,11 +45,16 @@
const std::string& challengeStatus, const std::string& challengeType,
const std::string& challengeTp, int remainingTime, int remainingTries,
const JsonSection& challengeSecrets, const security::v2::Certificate& cert);
+
+ void
+ setProbeToken(const std::shared_ptr<Data>& probeToken);
+
public:
Name m_caName;
std::string m_requestId = "";
int m_status = -1;
security::v2::Certificate m_cert;
+ std::shared_ptr<Data> m_probeToken = nullptr;
std::string m_challengeStatus = "";
std::string m_challengeType = "";
diff --git a/src/challenge-module/challenge-email.cpp b/src/challenge-module/challenge-email.cpp
index 8c49726..793eed7 100644
--- a/src/challenge-module/challenge-email.cpp
+++ b/src/challenge-module/challenge-email.cpp
@@ -19,6 +19,7 @@
*/
#include "challenge-email.hpp"
+#include "../ca-module.hpp"
#include "../logging.hpp"
#include <regex>
@@ -57,6 +58,16 @@
request.m_challengeStatus = FAILURE_INVALID_EMAIL;
return;
}
+ // check whether this email is the same as the one used in PROBE
+ if (request.m_probeToken != nullptr) {
+ const auto& content = request.m_probeToken->getContent();
+ const auto& json = CaModule::jsonFromBlock(content);
+ const auto& expectedEmail = json.get("email", "");
+ Name expectedPrefix(json.get(JSON_CA_NAME, ""));
+ if (expectedEmail != emailAddress || !expectedPrefix.isPrefixOf(request.m_cert.getName())) {
+ return;
+ }
+ }
request.m_status = STATUS_CHALLENGE;
request.m_challengeStatus = NEED_CODE;
request.m_challengeType = CHALLENGE_TYPE;
diff --git a/src/client-module.cpp b/src/client-module.cpp
index 29898bc..d107114 100644
--- a/src/client-module.cpp
+++ b/src/client-module.cpp
@@ -116,7 +116,7 @@
shared_ptr<Interest>
ClientModule::generateNewInterest(const time::system_clock::TimePoint& notBefore,
const time::system_clock::TimePoint& notAfter,
- const Name& identityName)
+ const Name& identityName, const shared_ptr<Data>& probeToken)
{
// Name requestedName = identityName;
if (!identityName.empty()) { // if identityName is not empty, find the corresponding CA
@@ -170,7 +170,7 @@
auto interest = make_shared<Interest>(interestName);
interest->setMustBeFresh(true);
interest->setCanBePrefix(false);
- interest->setApplicationParameters(paramFromJson(genNewRequestJson(m_ecdh.getBase64PubKey(), certRequest)));
+ interest->setApplicationParameters(paramFromJson(genNewRequestJson(m_ecdh.getBase64PubKey(), certRequest, probeToken)));
// sign the Interest packet
m_keyChain.sign(*interest, signingByKey(m_key.getName()));
@@ -341,7 +341,8 @@
}
const JsonSection
-ClientModule::genNewRequestJson(const std::string& ecdhPub, const security::v2::Certificate& certRequest)
+ClientModule::genNewRequestJson(const std::string& ecdhPub, const security::v2::Certificate& certRequest,
+ const shared_ptr<Data>& probeToken)
{
JsonSection root;
std::stringstream ss;
@@ -356,6 +357,23 @@
}
root.put(JSON_CLIENT_ECDH, ecdhPub);
root.put(JSON_CLIENT_CERT_REQ, ss.str());
+ if (probeToken != nullptr) {
+ // clear the stringstream
+ ss.str("");
+ ss.clear();
+ // transform the probe data into a base64 string
+ try {
+ security::transform::bufferSource(probeToken->wireEncode().wire(), probeToken->wireEncode().size())
+ >> security::transform::base64Encode(true)
+ >> security::transform::streamSink(ss);
+ }
+ catch (const security::transform::Error& e) {
+ _LOG_ERROR("Cannot convert self-signed cert into BASE64 string " << e.what());
+ return root;
+ }
+ // add the token into the JSON
+ root.put("probe-token", ss.str());
+ }
return root;
}
diff --git a/src/client-module.hpp b/src/client-module.hpp
index 30cd333..388ad85 100644
--- a/src/client-module.hpp
+++ b/src/client-module.hpp
@@ -89,7 +89,7 @@
shared_ptr<Interest>
generateNewInterest(const time::system_clock::TimePoint& notBefore,
const time::system_clock::TimePoint& notAfter,
- const Name& identityName = Name());
+ const Name& identityName = Name(), const shared_ptr<Data>& probeToken = nullptr);
std::list<std::string>
onNewResponse(const Data& reply);
@@ -124,7 +124,8 @@
genProbeRequestJson(const ClientCaItem& ca, const std::string& probeInfo);
const JsonSection
- genNewRequestJson(const std::string& ecdhPub, const security::v2::Certificate& certRequest);
+ genNewRequestJson(const std::string& ecdhPub, const security::v2::Certificate& certRequest,
+ const shared_ptr<Data>& probeToken = nullptr);
PUBLIC_WITH_TESTS_ELSE_PRIVATE:
ClientConfig m_config;
diff --git a/tests/unit-tests/ca-module.t.cpp b/tests/unit-tests/ca-module.t.cpp
index 45600b8..a3e5f01 100644
--- a/tests/unit-tests/ca-module.t.cpp
+++ b/tests/unit-tests/ca-module.t.cpp
@@ -162,8 +162,10 @@
item.m_caName = Name("/ndn");
item.m_anchor = cert;
client.getClientConf().m_caItems.push_back(item);
+
auto interest = client.generateNewInterest(time::system_clock::now(),
- time::system_clock::now() + time::days(10), Name("/ndn/zhiyi"));
+ time::system_clock::now() + time::days(10),
+ Name("/ndn/zhiyi"));
int count = 0;
face.onSendData.connect([&] (const Data& response) {
@@ -185,6 +187,40 @@
BOOST_CHECK_EQUAL(count, 1);
}
+BOOST_AUTO_TEST_CASE(HandleNewWithProbeToken)
+{
+ auto identity = addIdentity(Name("/ndn"));
+ auto key = identity.getDefaultKey();
+ auto cert = key.getDefaultCertificate();
+
+ util::DummyClientFace face(m_io, {true, true});
+ CaModule ca(face, m_keyChain, "tests/unit-tests/ca.conf.test");
+ advanceClocks(time::milliseconds(20), 60);
+
+ ClientModule client(m_keyChain);
+ ClientCaItem item;
+ item.m_caName = Name("/ndn");
+ item.m_anchor = cert;
+ client.getClientConf().m_caItems.push_back(item);
+
+ auto data = make_shared<Data>(Name("/ndn/CA/probe/123"));
+ m_keyChain.sign(*data, signingByIdentity(ca.m_config.m_caName));
+
+ auto interest = client.generateNewInterest(time::system_clock::now(),
+ time::system_clock::now() + time::days(10),
+ Name("/ndn/zhiyi"), data);
+
+ int count = 0;
+ face.onSendData.connect([&] (const Data& response) {
+ count++;
+ BOOST_CHECK(security::verifySignature(response, cert));
+ });
+ face.receive(*interest);
+
+ advanceClocks(time::milliseconds(20), 60);
+ BOOST_CHECK_EQUAL(count, 1);
+}
+
BOOST_AUTO_TEST_CASE(HandleChallenge)
{
auto identity = addIdentity(Name("/ndn"));
@@ -204,6 +240,7 @@
auto newInterest = client.generateNewInterest(time::system_clock::now(),
time::system_clock::now() + time::days(10), Name("/ndn/zhiyi"));
+ std::cout << "hi there" << std::endl;
// generate CHALLENGE Interest
ChallengePin pinChallenge;
shared_ptr<Interest> challengeInterest = nullptr;