1

RFC4226 describes the HOTP algorithm to "based on an increasing counter value and a static symmetric key known only to the token and the validation service", specifically:

HOTP(K,C) = Truncate(HMAC-SHA-1(K,C))

How would one go about storing the counter (i.e. C part of the algorithm), assuming that each user would have their own unique counter?

monotonous
  • 23
  • 3
  • 1
    Are you talking about the index into a list of passwords for One-Time-Passwords, or something else? – Out of Band Nov 06 '16 at 11:05
  • I'm referring to the C part of `HOTP = HMAC-SHA1(K, C)`, which RFC4226 describes this as an algorithm that is "based on an increasing counter value and a static symmetric key". – monotonous Nov 06 '16 at 13:31
  • Sorry for the confusion, I edited the question to be more detailed. – monotonous Nov 06 '16 at 13:38
  • ok, based on that my answer is a bit off now, but you might still be interested in the Lamport solution. – Out of Band Nov 06 '16 at 13:45
  • Each user will not only have a possibly different counter, but also preferably a different secret. ;-) – U. Windl Nov 05 '21 at 12:00

2 Answers2

2

An answer to your question will depend on what exactly you're using the counter value for.

There are various OTP schemes. One, HOTP, is using a counter, so maybe you're talking about an HOTP implementation. However, your question could also imply that you have a list of valid passwords lying around and you need to remember which one is the next valid one. This would strike me as a bad idea altogether and you can easily improve on it and solve your counter problem at the same time. So if you aren't talking about an HOTP counter or a similar solution, here's a suggestion:

Destroy used-up passwords

There is no need to keep around passwords which are already "used up". I can't immediately see why keeping old passwords around might be a problem, since they're no longer used, but if there is no need to keep them, I would make sure to destroy them. Depending on what you're using these passwords for, you might compromise forward security by keeping them around, for example.

If you throw away old passwords, you also fix the counter issue - you don't need to keep a counter at all because the next valid password is just the next one in your list. You might also get a bit of extra security by choosing one of the remaining passwords randomly instead of always taking the next one.

Other approaches

But if you don't need the random password selection, there are simpler solutions is an easier solution doesn't require you to store a list of passwords, for example

  • HOTP
  • TOTP
  • Lamport OTP

HOTP is basically building a hmac given a secret and a counter and using the result as a password. TOTP uses the time instead, so it can't be used with paper lists of passwords, but has the advantage of making it possible to expire OTPs.

Securing HOTP server-side

Edit: Added this section to better answer the updated/edited question

I think what you need to worry about when using HOTP is not whether to keep the counter secure, but how to keep the secret secure that you're using. If an attacker gains access to the secret key, he/she can generate all past and future passwords. With this knowledge, he might guess a range of counter values to try. Knowledge of the counter value itself is less dangerous, since without the secret, you can't produce a valid password.

So it seems to me that since you already need a solution to store the secret key securely, you could just err on the side of caution and store the counter value in the same way. If you don't want that, I'd simply add an additional column to my user table in the database and store the counter value there.

You could encrypt this counter value beforehand using another password (and random iv, so identical counter values don't produce identical ciphertexts!), but frankly I don't see much gain for the following reasons:

  1. The counter value itself isn't helpful to an attacker unless he already has the user's password list or your secret key, and in both cases, you're pretty much screwed anyway. (One exception: With knowledge of the counter, an attacker might social-engineer the coresponding password from a user, using his knowledge of which password came next to convince the user that he was legit)
  2. Encrypting will only protect the counter when someone manages to steal your database, but can't steal the encryption key or run the decryption code on his behalf. I think that's an unlikely scenario.

Securing sensitive server state by not storing anything sensitive

Lamport OTP is a simple, beautiful solution to OTPs which doesn't need a shared secret and which you might also consider.

Basically, the idea is to hash a hash of a hash of a hash.... For a simple idea on how it works, consider the following OTP solution: Take a secure password hash function H(x), and hash an initial random seed secret S which only you know. To generate the list of passwords for the user, take the result and hash it again, and again, and again, as many times as is necessary to yield the required number of passwords. So basically you're building a list of OTPS:

[ H(S), H(H(S)), H(H(H(S))), ...]

Now one problem you have is that if you use the hashes directly as passwords, if an attacker learns just one of them, he can calculate all the future ones, too.

You can fix this in a variety of ways. For example, you could take the resulting hash and use it as a password to encrypt another secret string, and then use the encrypted string as the password to put on the list and verify against. (I just made that up - don't use this without carefully thinking about whether this is secure, as it probably isn't).

Lamport OTP does it way more clever. It's basically sending the hashes backwards; e.g. you calculate the first 100 hashes based on a seed and give the user a list of them in reverse order. So the first one he sends is number 100.

The next one he sends is number 99. The server can now check if H(OTP99) = OTP100. Because Hash functions are one-way, knowing OTP 100 doesn't help an attacker to get at OTP99.

The nice thing here is that the server doesn't need to store anything that can be compromised. It must keep the last sent password (e.g. hash) stored somewhere, so in the beginning it must have the value for OTP101, and when the user successfully logs in with OTP100, it must then replace OTP101 by OTP100, but even if an attacker gets to know the currently stored value at the server side, it won't help him, since what he actually needs to know is S (or any of the derived hashes), and they're hopefully secure at the client. The server never needs to store S at all, meaning that after you generate the list of passwords and OTP101 to store at the server, you no longer need to know S or any of OTPs 1-100. This strikes me as a very beautiful solution which completely sidesteps your question of whether you need to keep the counter value secure - with Lamport, there isn't anything to keep secure at the server side.

I've simplified a bit, read up on the algorithm/protocol to see how to implement this in detail.

Out of Band
  • 9,150
  • 1
  • 21
  • 30
0

As @Pascal pointed out you should encrypt the Key "K". But you also need to think about where to store the encryption key.

Simple Approach

Use an encryption key on your system. The encryption key is a file on your harddisk. It is used to encrypt the K in the database. The database might be located on another machine, managed by the DBMS admin. This way you can be sure, that the stolen database does not compromize the Ks of the tokens.

But if you are running on the same machine, you can steal the encrypted K and the encryption key.

Thus, you should harden your server and control access to it.

More secure for offline attacks

If you are running on the same machine, you can encrypt the encryption key. Use a password to encrypt the encryption key. When you start your service you can provide the password and thus the encrpytion key gets decrypted in RAM and can be used to decrypt the Ks in the database.

Most sophisticated apporach

Use a Hardware Securiy Module for storing the encryption key. Have the K be decrypted inside the HSM. You can use any HSM.

Even more than most sophisticated

Use a generic HSM which you can program to do HOTP. This way you can keep all Ks within the HSM. They never need to be decrypted in real live. Have the HSM calculate the next OTP value for a given counter. You can use custom programmable HSMs.

cornelinux
  • 1,993
  • 8
  • 11
  • Good thoughts. One important detail: The most sophisticated approach using an HSM has the problem that if an attacker gains access to your server, he'll most likely also gain access to the HSM. Not the encryption key which is stored inside the HSM, but the ability to pass in your encrypted HOTP key K and get out the decrypted key. The HSM really only helps you when it knows how to do HOTP (the even more sophisticated approach). And even then, an attacker will be able to pass in counter values and get out OTP values. – Out of Band Nov 07 '16 at 13:58
  • You are right. If you have root access you could *decrypt* the keys one after another. If you had access to the HSM. This very much depends on the HSM how it grants decryption permission, based on a password or token. The access credentials for the HSM should be kept in memory - i.e. entered during service startup time. Again you could monitor the traffic, but this is the difference between compromizing *some* *K*s while the attacker is ON the system or *all* *K*s at once. – cornelinux Nov 07 '16 at 15:54