Author

Topic: Can HMAC be used for 2FA? (Read 1063 times)

legendary
Activity: 3416
Merit: 1912
The Concierge of Crypto
September 08, 2013, 08:23:38 AM
#5
@fpgaminer, thanks for the lengthy post. I did not completely understand it, but I get the idea. It can work, but it might be vulnerable. HMAC isn't that much harder to implement (and there are tons of libraries out there to use.)

I have another question, but it might be off-topic, but hey, I started this topic so... but it's really off-topic so I will just PM you. It has to do with all these bitcoin gambling games and how they all use SHA-256 or SHA-512 and HMAC of them to roll results.
hero member
Activity: 560
Merit: 517
September 08, 2013, 03:53:28 AM
#4
Quote
Another case of "Hey, I have an idea" but someone already invented it 30 years ago.
Smiley  That will be true in general.  The key is that, while the idea is important, the implementation of the idea is more meaningful.  For example, idea of capacitive touch screens was invented over forty years ago, but it wasn't until recently that they become good enough to be used ubiquitously.  A better example, Bitcoin.  Cryptomoney has been around for quite awhile as well, but it was never implemented as successfully as Bitcoin (having a blockchain being the breakthrough).

Quote
I understand that HMAC would be better, but what's wrong with using the regular hash?
Sorry for the terse response, I was in a rush at the time.  Let's create a more general example, so I can better demonstrate why using SHA256 as a MAC is insecure.  This is going to be a long one, but by your request I wrote it out long-wise to be educational.

Suppose you have a chatroom server, and it receives messages from clients.  The clients send along their username, and message.  You want to ensure the messages actually belong to the given username.  If you didn't, users could impersonate one another.  So you establish a shared secret, unique to each user, and give it to each user when they register.  Now, when clients send a message, they send their username, message, and a MAC.  MAC is calculated like so:

Code:
mac = SHA256 (secret + message)

On the server side, the server can lookup the secret for the specified username, calculate what the MAC would be for the given message, and compare the calculated MAC to the one given by the client.  If they match, all is well.  Seems reasonable.  Given that SHA256 is a one-way function, how could anyone forge a MAC?  Well ...

Suppose a client, named Alice, sends the message "Hello" to the server.  Mallory, an attacker, intercepts this message and its MAC.  Now Mallory can use this information to impersonate Alice.  Recall the SHA256 algorithm:

Code:
def sha256 (message):
     data = pre_processing (message)  # Does a specific kind of padding to bring the data length to a multiple of 512-bits
     blocks = split (message)   # Split into 512-bit blocks
     state = initial_state  # Specified by SHA-2 standard
     
     for block in blocks:
          state = process_block (state, block)

     return state

For the message "Hello", Alice would call sha256 ("password" + "Hello") to calculate the MAC.  The data given to SHA256 is 104 bits long.  So the code boils down to this:

Code:
mac = process_block (initial_state, "password" + "Hello" + 408_bits_of_padding)

The server would do the same thing, and verify the message's owner.  How about Mallory?  Mallory doesn't know "password", but he does know "Hello", he knows the MAC Alice calculated, and suppose he knows the length of "password" (it could be a fixed length key, like in TOTP).  So, time for a fun trick!

Code:
state = mac
new_mac = process_block (state, ". I'm stupid" + 416_bits_of_padding)
new_message = "Hello" + 408_bits_of_padding + ". I'm stupid"

Now, Mallory sends new_message and new_mac to the server.  What will the server do?  sha256 ("password" + new_message).  Let's follow how it will execute the SHA256 algorithm:

Code:
data = pre_processing ("password" + new_message)
blocks = split (data)
state = initial_state

for block in blocks:
     state = process_block (state, block)

if state == new_mac:
     return True
return False

We know ("password" + new_message) is 608 bits long, thus requiring 416 bits of padding, so we can simplify the code:

Code:
data = "password" + new_message + 416_bits_of_padding
blocks = split (data)  # Will be two blocks

state = process_block (initial_state, blocks[0])
state = process_block (state, blocks[1])

if state == new_mac:
     return True
return False

Perhaps things are beginning to look suspicious, but let's keep going, given that we know what new_message is, and given that split just splits along 512-bit boundaries:

Code:
blocks[0] = "password" + "Hello" + 408_bits_of_padding
blocks[1] = ". I'm stupid" + 416_bits_of_padding

state = process_block (initial_state, blocks[0])
state = process_block (state, blocks[1])

...

Uh oh ... look back a bit and look at how Alice calculated mac.  "mac = process_block (initial_state, "password" + "Hello" + 408_bits_of_padding)".  See how the data passed to process_block in Alice's calculation is equal to blocks[0] in the server-side calculation on Mallory's message?

Code:
state = process_block (initial_state, "password" + "Hello" + 408_bits_of_padding)
state = process_block (state, blocks[1])
...

So, the first state the server calculates is equal to the mac that Alice sent awhile ago.  The same mac Mallory intercepted and used in this attack.  So we simplify the server-side code again:

Code:
state = process_block (mac, ". I'm stupid" + 416_bits_of_padding)
...

Which, if you look back again, you'll see is equal to the calculation that Mallory performed.  Oh dear ...

Therefore, the server calculates the same MAC that Mallory sent for his manipulated message.  Therefore, the server accepts the message, and makes the world think that Alice is stupid.  Cry


As you can see, using SHA256 as a MAC function leaves one vulnerable to a message extension attack.  As you can also see, the message extension attack has a certain form: Mallory must always first append padding to his manipulated message, before tacking on his desired extra data.  So, Mallory can't send any arbitrary message.  That means this attack isn't always viable.  In your case, for TOTP, it probably can't be used.  That said, I still wouldn't use SHA256 in a TOTP implementation.  It's bad practice and asking for trouble.

Also, if I recall correctly, the HMAC construct has been shown to strengthen the underlying hash function.  For example, I think HMAC-SHA1 is not vulnerable to the same attacks that SHA1 has fallen prey to in recent years (SHA1 has been "broken"; as of 2012 it costs ~$3 million to break a SHA1 hash).

One of the golden rules in cryptography is to use an algorithm only for its intended purpose, never anywhere else without extensive research and vetting.  If you use sledgehammer to put a nail into a wall, it'll probably work, but more often than not you'll find yourself with a much bigger problem than a nail in the wall...

Looking for a good book?  Cryptography for Developers.  Ain't no developer got time for a cryptography degree.
legendary
Activity: 3416
Merit: 1912
The Concierge of Crypto
September 08, 2013, 12:22:11 AM
#3

Yeah, I was reading up on it last night, and this is sort of exactly what Google Authenticator does. I never knew, seriously. Another case of "Hey, I have an idea" but someone already invented it 30 years ago.

Quote
Quote
In fact, you can even use a regular hash function without HMAC, just by concatenating the two values.
Do not do that.

I understand that HMAC would be better, but what's wrong with using the regular hash?

SHA-256("secret" + time)
vs
HMAC-SHA-256("secret", time)

Isn't it that any single change in the input of a hash, results in vastly different hash output?

sha256("secret-10:30:31") = d042a6a52aa1dc9df60bef3daa8e99a26ac7fddf03dbc7e0ccf8fb853c08828c
sha256("secret-10:30:32") = ccce19752b492ed228dc09f33b87a9a4f259b2e5c7978446361408a0e04c7d08
sha256("secret-10:30:33") = d21eac2fa6c4a09b0fcc0f437e63189a5dbf2c926901d81ec888a5214ea66712

? I'm missing something, please school me (I'm serious), I want to understand why HMAC is better or why the non-HMAC method I just showed above is not acceptable.
hero member
Activity: 560
Merit: 517
September 07, 2013, 08:24:17 PM
#2
Beginner's Guide to TOTP

Quote
In fact, you can even use a regular hash function without HMAC, just by concatenating the two values.
Do not do that.
legendary
Activity: 3416
Merit: 1912
The Concierge of Crypto
September 07, 2013, 10:08:32 AM
#1
I was just looking at HMAC.

It requires two inputs, a secret key and something else. (And the hash function, of course)

Let's use SHA-256 or SHA-512.

Can you use that to roll your own 2FA? Two factor authentication?

Secret = some random string, maybe 64 characters or more.
Message = the current time, either accurate to the second, or in blocks of half a minute to a minute.

In fact, you can even use a regular hash function without HMAC, just by concatenating the two values.

Right? I've never bothered to look into how Google Auth works, but does my idea make sense or is it too simple? The hash result is of course condensed or truncated to the usual 6 digit number for the usual 2FA. The site would just compare the inputed code to the last 30 seconds to 60, before and after the current time.
Jump to: