Table of Contents

Reliable, Secure and Universal Backup for U2F Token

I really love the security level U2F provides, but together with security, we have to think carefully about the backup and recovery plan. It would totally suck to get locked out of my accounts if something happens with my U2F token. At the same time, it doesn't make sense to have a backup which compromises U2F security.

The advice I've seen in many places is that the best practice is having a second U2F token for backup, which should be manually added to every service and which should be kept in some “safe” place. And another common practice is to have some non-U2F method as a backup (OTP, or recovery codes, etc). Well, both of those leave a lot to be desired, honestly.

Let's talk about the first option, the “best” one: having a backup token.

Separate U2F token for backup

Having a second token means that whenever I register on a new service, I have to add both of my tokens to my account. It poses a few problems:

Clearly this “best practice” is not so secure and also quite burdensome. So let's look at another common option (spoiler: which sucks even more).

Non-U2F method as a backup

OTP:

Recovery codes:

So bottom line, all of the above methods are non-universal, burdensome and not so secure.

Optimal backup strategy

Now that I ranted enough on why everything is so bad, let me tell what I want. I do actually want to have two U2F tokens: primary and backup, but they should be set up in a special way:

Before we discuss the technical possibility of it, let me elaborate why I actually want that and how I'd use it.

Why it is awesome

If we refer to my points above on why a distinct U2F backup token is a bad idea, we can see that all shortcomings of such approach are solved:

And if I'm unlucky enough to actually lose my primary token somehow, here's what I should do:

I consider this strategy overall to be a really good tradeoff for being secure and still have a reliable, burden-free backup. It's both more reliable and more secure than all other backup strategies.

Implementation

Brief overview of U2F

Before we can talk about actually making it a reality, we need to understand briefly how U2F works. So, here's how it's implemented by the majority of manufacturers (there are some details which are not specified in FIDO U2F, and thus are implementation details)

A U2F token device has a device_secret programmed into it, together with a 32-bit counter which starts from 1 and can only be incremented. When we register that device on some new service, here's what happens:

And authentication goes as follows:

Curious reader can refer to FIDO Alliance Proposed Standard 11 April 2017 and Yubico's developer documentation for more details.

Let me highlight the parts which are very relevant to our today's discussion.

Relevant parts

First, the token doesn't store service_private_keys for each service: instead, it derives keys from other data it has using HMAC-SHA256. This is very important for us: obviously if each device was keeping its own set of per-service private keys, it would be technically not possible to use the backup token on a service without actually registering that same physical token on that service.

(By the way, this is not actually a U2F requirement: U2F doesn't specify how to store keys, and at least in the past there were some implementations which were keeping private keys on the device, e.g. see this github issue. And the rationale for not storing private keys was to avoid limiting the maximum number of services registered with a given token: since device doesn't store any per-service data, the number of services is unlimited)

And second, the token has a counter to prevent device clones.

At first sight, it might seem that this counter thing prevents us from implementing the desired strategy (at least it seemed so to me when I was trying to come up with some solution), but in fact, it only helps us! Bear with me.

The main idea

The idea is as follows: at the time of manufacturing, program two tokens so that they share the same device_secret, but the backup token needs to have a small tweak: instead of returning counter from 1 (as normal tokens do), it should add some large constant to each returned value: I'd say, approx. half of the 32-bit range, e.g. 2 000 000 000, looks reasonable: I'm unlikely to exceed it during the whole life.

That's basically it, simple and effective.

Having two tokens programmed this way, I hide the backup token somewhere far away and never use it, and just keep using the primary one, so the counter will start from 1 and will be incremented by 1 at each authentication. If something terrible happens and the primary token is gone, I go all the way down to get the backup token from where I hidden it, and I can immediately use it on all the services I used the primary one with, because it has the same secret, and its initial counter is set to a very large number, which I'm unlikely to exceed during the lifetime anyway. Some service's admin might get surprised if they happen to look at authentication counter logs when they see a number suddenly jumping from some small value to 2 milliards, but that shouldn't be a concern. :)

Also let me clarify that I'm not suggesting making the tokens cloneable. As you see, they are not entirely clones anyway (because the backup token has different counter); they only share device_secret which needs to be just programmed to them at the same time, and after that it's not possible to read that data from the token or otherwise make a clone.

Caveat with the counter

Attentive reader could notice that there is some security flaw in the idea above: what if attacker gains access to the primary token, and then somehow forces it to count up to 2 000 000 000? Then they'll be able to use the token even after the backup token was activated.

Luckily, this issue has a simple solution. The counter should be implemented as some hardware counter (presumably on some cryptoprocessor), and that hardware counter should have the range less than 32-bit. E.g. on ATECC508A, monotonic counters can count only up to 2097151. So, by setting the boost counter value in the backup token to anything larger than 2097151 ensures that the primary token could never possibly use the counter value which larger than the backup's counter. To clarify: let's refer to the value from ATECC508A's counter as hw_counter. Then:

Note that we do not modify the actual hw_counter in ATECC508A; it will still count from 0 to 2097151. Instead, every time we need to get a counter value, we read hw_counter from ATECC508A, then add boost constant, and return further (for using in u2f calculations).

This way, the counter range of the primary token is [0, 2097151], while the counter range of the backup is [2000000000, 2002097151]. The fact that those ranges don't intersect ensures that once backup token is used on some service, the primary one is invalidated for good on that service.

Actual implementation

We cannot use YubiKeys for that. Even though YubiKeys do support programming of OTP keys easily (and they even have two slots for them), the U2F key material is totally set in stone. They say that the device_secret is generated on-chip at the time of manufacturing. Well, that's cool and secure and all, but as I mentioned in the beginning, along with security we have to think about a good recovery plan, otherwise one has to actually weaken that security with some back-way if they want at least some recovery plan. Okay, so no YubiKeys for me.

Ideally we should be able to program U2F key material and the counter boost value; having that, creating backups as explained would be easy. Also, it's actually a good thing to be able to control our keys: even though Yubico claims that they do not know the U2F keys in the devices they sell, we have no way to prove that. At the moment, there are no U2F manufacturers who would allow us to program U2F keys and counter Actually, there is! See the section on Trezor below.

Luckily, there is an open source implementation of U2F: U2F Zero. Hats off to Conor Patrick for that! I totally appreciate it, thank you Conor.

There are detailed instructions on reprogramming the devices on their wiki: Building a U2F Token, read that page carefully.

I'm not a hardware guy and I'm not excited about soldering parts together, so I just ordered a few pre-made devices, and reprogrammed them. I personally used a programmer by Silicon Labs for that, but there are alternatives, check the link above and the programmer datasheet. GND is pins 2, 3 or 9; C2D is pin 4, C2CK is pin 7.

At the device configuring stage, we need to specify the same keys when programming both primary and backup tokens. At the moment of writing it, the tools in the u2f-zero repo don't support that (they can only generate pseudo-random keys), so I forked it and added a small commit which implements a few flags. I'll make a pull request and hopefully it will be merged in some shape, but for now, use my fork if you need backups as discussed: https://github.com/dimonomid/u2f-zero .

So, to program two tokens, the sequence is very similar to what's described in the wiki page linked above; the only differences are:

$ MY_WKEY=$(dd if=/dev/urandom bs=1 count=32 | od -t x1 -An | tr -d '\n ')
$ MY_RKEY=$(dd if=/dev/urandom bs=1 count=32 | od -t x1 -An | tr -d '\n ')
$ ./setup_device.sh -w ${MY_WKEY} -r ${MY_RKEY} gencert/ca/key.pem gencert/ca/cert.der
uint32_t u2f_count()
{
	struct atecc_response res;
	atecc_send_recv(ATECC_CMD_COUNTER,
			ATECC_COUNTER_INC, ATECC_COUNTER0,NULL,0,
			appdata.tmp, sizeof(appdata.tmp), &res);
	return le32toh(*(uint32_t*)res.buf);
}

For backup token, the last line should be modified as follows (obviously the exact boost value is up to you, I used 2000000000 here) :

	return le32toh(*(uint32_t*)res.buf) + 2000000000;

Everything else is the same as explained in the u2f-zero wiki. To outline the overall process real quick:

That's it! Go to https://demo.yubico.com/u2f , register your primary token, then authenticate with it, and click on the “Techical data” button: on the bottom there will be a counter with some small value. Then try to authenticate again but with your backup token, it'll let you in and you'll see the boosted counter.

I verified that if I add the primary token on Google account, then use it for authentication, then use backup token, and after that if I try to use the primary one again, it won't work (because of the counter). So, as desired, the first usage of the backup invalidates the primary token. Github does the same, and supposedly does any other sane service which supports U2F. Awwwwww. <3

Trezor

This is a new section I added a year after writing the original article.

Luckily, Trezor now supports U2F as well, so one can program their own key using the BIP39 recovery seed, and that key will be used for U2F.

At the time of device reset, one can specify custom counter as well, like this:

$ trezorctl reset-device -t 256 --u2f-counter 2000000

(more details on trezorctl is available in their wiki)

Therefore, one could have two Trezor devices set up with the same recovery seed, but different counters, and achieve the desired backup goal. Or, if having a readily-available backup device isn't a concern, just backing up the recovery seed is enough.

A word of caution

Even though I keep saying that the backup strategy is awesome (isn't it?!), I'm not so sure about the particular implementation of it, i.e. by means of u2f-zero or Trezor. u2f-zero is the only way I could implement it at the time of writing, so that's what I did, but if we imagine that a hacker gets physical access to the u2f-zero device, I'm not sure whether hacking it (i.e. getting secret key from it) would be as hard as hacking a YubiKey. U2f-zero guys claim that “the security level is about the same as a modern car key”, but I'd need to do a more thorough review of the implementation to judge whether it's true.

The same is true for Trezor as well, unfortunately: Kraken Security Labs were able to successfully extract seeds from all Trezor models.

Nevertheless, to tell the truth, I'm not too worried about it. If an attacker just steals the token without an intention to return it back, then it doesn't matter how hard it is to hack the token, because the attacker can then just use it to log into my accounts and then do whatever they want, like add another token, revoke this one, etc. However, for that I have to be already compromised otherwise as well, because the u2f token is only a second factor.

So, the only scenario in which u2f-zero or Trezor can be less secure than e.g. yubikey is when the attacker tries to get access to my u2f device for some short period of time, learn the secret key from it, and then return the device back to me, all without me noticing anything. For that to be done, one would have to read the contents of the MCU flash (or RAM in the right moment), which isn't impossible of course but isn't trivial either, especially in the short period of time they have until I could notice that I got my key stolen or replaced by another one. Just to be safe, I keep verifying almost every day that the key in my pocket is legit.

Considering all that, I still believe that having a backup as described by means of u2f-zero or Trezor is a lot more secure and reliable than having backup by any other means. At least for me.

Conclusion

The discussed backup strategy beats all alternatives on every dimension: it's universal, it's more secure and more reliable than any other backup method.

I'm really surprised that manufacturers don't use it. It would be nice to have an option to buy a ready pair of tokens which are set up in this way, but alas. A guy from YubiKey support, James A., even said to me that the backup the way I want is “not possible with the way U2F is designed”. Well, not that impossible, luckily.

In case you're interested in purchasing a few pre-made u2f-zeros, here are links:

Donate
Discuss on Hacker News, Reddit, Lobsters, or right here: