Renew challenge on expiry

This commit is contained in:
Christoph Hagen 2023-12-08 00:24:15 +01:00
parent 0a11d9ff27
commit 1fe03a6906
3 changed files with 32 additions and 9 deletions

View File

@ -113,6 +113,17 @@ private:
*/ */
void processMessage(SignedMessage* message); void processMessage(SignedMessage* message);
/**
* @brief Checks that the message is valid and prepares a challenge.
*
* This function is also called when a challenge response arrives too late.
*
* @param message The message to respond to
*
* Note: Prepares the response in the outgoing message buffer.
*/
void checkAndPrepareChallenge(Message* message);
/** /**
* @brief Prepare a server challenge for a local or socket message. * @brief Prepare a server challenge for a local or socket message.
* *

View File

@ -59,8 +59,11 @@ enum class MessageResult: uint8_t {
/// @brief A message is already being processed /// @brief A message is already being processed
TooManyRequests = 8, TooManyRequests = 8,
/// @brief The received message result was invalid
InvalidMessageResult = 9,
/// @brief An invalid Url parameter was set sending a message to the device over a local connection
InvalidUrlParameter = 10, InvalidUrlParameter = 10,
InvalidResponseAuthentication = 11,
}; };

View File

@ -116,7 +116,7 @@ void SesameController::sendPreparedResponseToServer() {
void SesameController::processMessage(SignedMessage* message) { void SesameController::processMessage(SignedMessage* message) {
// Result must be empty // Result must be empty
if (message->message.result != MessageResult::MessageAccepted) { if (message->message.result != MessageResult::MessageAccepted) {
prepareResponseBuffer(MessageResult::ClientChallengeInvalid); prepareResponseBuffer(MessageResult::InvalidMessageResult);
return; return;
} }
if (!isAuthenticMessage(message, keyConfig.remoteKey)) { if (!isAuthenticMessage(message, keyConfig.remoteKey)) {
@ -125,7 +125,7 @@ void SesameController::processMessage(SignedMessage* message) {
} }
switch (message->message.messageType) { switch (message->message.messageType) {
case MessageType::initial: case MessageType::initial:
prepareChallenge(&message->message); checkAndPrepareChallenge(&message->message);
return; return;
case MessageType::request: case MessageType::request:
completeUnlockRequest(&message->message); completeUnlockRequest(&message->message);
@ -136,12 +136,16 @@ void SesameController::processMessage(SignedMessage* message) {
} }
} }
void SesameController::prepareChallenge(Message* message) { void SesameController::checkAndPrepareChallenge(Message* message) {
// Server challenge must be empty // Server challenge must be empty
if (message->serverChallenge != 0) { if (message->serverChallenge != 0) {
prepareResponseBuffer(MessageResult::ClientChallengeInvalid); prepareResponseBuffer(MessageResult::ClientChallengeInvalid);
return; return;
} }
prepareChallenge(message);
}
void SesameController::prepareChallenge(Message* message) {
if (hasCurrentChallenge()) { if (hasCurrentChallenge()) {
Serial.println("[INFO] Overwriting old challenge"); Serial.println("[INFO] Overwriting old challenge");
} }
@ -155,10 +159,6 @@ void SesameController::prepareChallenge(Message* message) {
} }
void SesameController::completeUnlockRequest(Message* message) { void SesameController::completeUnlockRequest(Message* message) {
if (!hasCurrentChallenge()) {
prepareResponseBuffer(MessageResult::ClientChallengeInvalid, message);
return;
}
// Client and server challenge must match // Client and server challenge must match
if (message->clientChallenge != currentClientChallenge) { if (message->clientChallenge != currentClientChallenge) {
prepareResponseBuffer(MessageResult::ClientChallengeInvalid, message); prepareResponseBuffer(MessageResult::ClientChallengeInvalid, message);
@ -168,6 +168,15 @@ void SesameController::completeUnlockRequest(Message* message) {
prepareResponseBuffer(MessageResult::ServerChallengeMismatch, message); prepareResponseBuffer(MessageResult::ServerChallengeMismatch, message);
return; return;
} }
if (!hasCurrentChallenge()) {
// Directly send new challenge on expiry, since rest of message is valid
// This allows the remote to directly try again without requesting a new challenge.
// Security note: The client nonce is reused in this case, but an attacker would still
// not be able to create a valid unlock request due to the new server nonce.
prepareChallenge(message);
return;
}
clearCurrentChallenge(); clearCurrentChallenge();
// Move servo // Move servo