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