Very simple and minimal replay prevention scheme Suppose a desktop client communicates with an internet server program via HTTP requests.

I have a symmetric secret key shared between them used for encrypting/securing communication between them. 

I also mention that communication is almost one-way i.e almost always it is the client that sends some data to the server and server returns no data to the client except reporting unexpected errors like decryption failures. In my program the responses of the server aren't even (still) encrypted (although i can implement it relatively easily if really necessary - tell me the reason to do so) because the client makes no important action/decision upon receiving thease error responses (any response other than empty string is regarded as an error and is just ignored (at most printed/logged)).

My app is not very serious/security critical. It is just a little private program (and somewhat experimental/educational and I don't want to go further from a point in its security because I don't have enough reason/priority/time for implementing more details). So please don't be stringent about non-critical details. for example I know that the shared/master key shouldn't be used directly and each time a new session key should be generated somehow, but nonetheless i am using the master key directly for every encryption forever. My intended security is the bare minimum that is absolutely necessary so that ordinary hackers cannot misuse it or read my private data in transit, assuming the client and server environments are out of their reach.

So far i have implemented encryption (AES128(CBC)+HMAC-SHA256), but it seems to me that replay prevention is almost necessary too (and implementing it isn't much work and doesn't need much modifications to my existing codes). So i want to add replay prevention too, but in the most simple way possible for my app and its minimal security requirements.

I thought of a scheme like this:


Client stores a counter initially at 0
With each request this counter is incremented by one and sent along other data (counter is encrypted too or at least authenticated by MAC)
Obviously, server stores the last counter value(plus 1) it received and rejects every later request with a smaller counter value.
When client counter resets to zero again, e.g because of a reinstallation, thus it sends a request with an invalid counter to the server, server rejects and responds with an error message to the client and tells in this response the minimum counter value expected.
Client then sets the counter to the value reported by the server and sends the request again (Seems to me that the error response/counter value returned by the server doesn't even need to be encrypted/authenticated, because any other counter value will be rejected by the server again).


And, yes I am aware of the problem of counter variable reaching the end of its range (maximum) and reseting to 0. I will take care of it later (seems a simple problem to sovle)

Is this scheme correct in your opinion?
The main part of the scheme works, but your reset mechanism in the client has a potential weakness:


First, the attacker can easily get the information about what is the next expected counter, by simply replaying any old message and inercepting the answer.
Then, at some counter $c$, the attacker intercepts the message $m$, and saves it. If your answers are just unencrypted and not signed, he can easily send your client a fake-reply.
All subsequent messages are also intercepted, until the client is shut down.


And now the client is restarted, and does the "ask for the correct counter" by sending a message with counter 0. The return value will be something smaller than $c$, because the server never received that value or anything higher. At this point, the attacker could switch the new data for the old data instead. That isn't a replay attack, but the client might think he send something different than what was received at the server.

More complex, the server could actually keep track of the counter, especially if it is always increased by 1. And then replace new information with old ones.



The problem with your idea is, that the counter only changes after receiving a message. But if no message is delivered, the sender might send several messages with that same expected value in the counter. And then the attacker can choose which one he wants to deliver to the server.

A common mechanism to counter replay attacks is to have a challenge-response mechanism: The client sends a hello-message to the server, the server replies with a random challenge, and then you use that number in the response.