Probe: add probe token to New and Challenge
Change-Id: Iae8b009bb2e78f03910e53fb49e750ebc8a6e6ae
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;