PIN Complexity proposal #187

Open
opened 2025-09-17 15:02:29 +08:00 by token2 · 30 comments
token2 commented 2025-09-17 15:02:29 +08:00 (Migrated from github.com)

We hereby propose a modification (tested and confirmed on a Pico2 device) to implement PIN complexity, similar to Token2 PIN+ devices. We did not submit a pull request for the modification to the original file (src/fido/cbor_client_pin.c), as this feature may not be required by all users. Instead, we are providing an alternative version for those who wish to add PIN complexity. Since the data is encrypted using the PIN, we recommend enforcing PIN complexity at the firmware level.

cbor_client_pin.c_PIN-Complexity.txt

We hereby propose a modification (tested and confirmed on a Pico2 device) to implement PIN complexity, similar to Token2 [PIN+](https://www.token2.com/site/page/token2-fido2-pin-see-the-pin-complexity-in-action) devices. We did not submit a pull request for the modification to the original file (src/fido/cbor_client_pin.c), as this feature may not be required by all users. Instead, we are providing an alternative version for those who wish to add PIN complexity. Since the data is encrypted using the PIN, we recommend enforcing PIN complexity at the firmware level. [cbor_client_pin.c_PIN-Complexity.txt](https://github.com/user-attachments/files/22378042/cbor_client_pin.c_PIN-Complexity.txt)
polhenarejos commented 2025-09-24 19:07:00 +08:00 (Migrated from github.com)

I saw your code and looks interesting. However I need to think on a way to enable/disable these particular features for the most of the public via pico-fido-tool/web commissioner.

I saw your code and looks interesting. However I need to think on a way to enable/disable these particular features for the most of the public via pico-fido-tool/web commissioner.
token2 commented 2025-09-26 22:10:02 +08:00 (Migrated from github.com)

I saw your code and looks interesting. However I need to think on a way to enable/disable these particular features for the most of the public via pico-fido-tool/web commissioner.

Thank you for considering this. Can you confirm whether a complex PIN is the only effective defense against an offline brute-force attack? If an attacker is able to extract a copy of the key’s flash (e.g. using picotool), they could run unlimited PIN guesses offline; by contrast, a standard FIDO2 token enforces an on-device attempt limit (typically 8 attempts).

> I saw your code and looks interesting. However I need to think on a way to enable/disable these particular features for the most of the public via pico-fido-tool/web commissioner. Thank you for considering this. Can you confirm whether a complex PIN is the only effective defense against an offline brute-force attack? If an attacker is able to extract a copy of the key’s flash (e.g. using picotool), they could run unlimited PIN guesses offline; by contrast, a standard FIDO2 token enforces an on-device attempt limit (typically 8 attempts).
polhenarejos commented 2025-09-26 22:14:24 +08:00 (Migrated from github.com)

This is not really true. RP2350 and ESP32 boards have OTP region, where a secret key is used to mask the PIN. Therefore, in practice, the for these boards the brute-force attack will need to randomize 256 bits, rather than the 64 of a typical 8-bytes PIN.

This is not really true. RP2350 and ESP32 boards have OTP region, where a secret key is used to mask the PIN. Therefore, in practice, the for these boards the brute-force attack will need to randomize 256 bits, rather than the 64 of a typical 8-bytes PIN.
token2 commented 2025-09-26 22:20:06 +08:00 (Migrated from github.com)

This is not really true. RP2350 and ESP32 boards have OTP region, where a secret key is used to mask the PIN. Therefore, in practice, the for these boards the brute-force attack will need to randomize 256 bits, rather than the 64 of a typical 8-bytes PIN.

Sorry for not being clear. Attack vector: I steal an RP2350 Pico‑FIDO key from the victim and dump its flash to another RP2350 device (keeping the flash dump in my files). I then plug that cloned device into Windows and perform a PIN‑guessing attack using the standard OS CTAP dialog (or libfido2). When the device locks after exhausting PIN attempts, I reset the cloned device and restore the flash dump, which gives me an additional eight guessing attempts.

> This is not really true. RP2350 and ESP32 boards have OTP region, where a secret key is used to mask the PIN. Therefore, in practice, the for these boards the brute-force attack will need to randomize 256 bits, rather than the 64 of a typical 8-bytes PIN. Sorry for not being clear. Attack vector: I steal an RP2350 Pico‑FIDO key from the victim and dump its flash to another RP2350 device (keeping the flash dump in my files). I then plug that cloned device into Windows and perform a PIN‑guessing attack using the standard OS CTAP dialog (or libfido2). When the device locks after exhausting PIN attempts, I reset the cloned device and restore the flash dump, which gives me an additional eight guessing attempts.
polhenarejos commented 2025-09-26 23:03:15 +08:00 (Migrated from github.com)

It will not work. You cannot clone OTP region, where the primary key is located. If you do that, a new fresh key will be initialized in the second board and thus, all data from flash will not be accessible (since the OTP key is different). No matter even if the PIN is correct, the data is corrupted.

It will not work. You cannot clone OTP region, where the primary key is located. If you do that, a new fresh key will be initialized in the second board and thus, all data from flash will not be accessible (since the OTP key is different). No matter even if the PIN is correct, the data is corrupted.
My1 commented 2025-09-26 23:48:35 +08:00 (Migrated from github.com)

stupid question, I am only really familiar with fido, not really with the microcontrollers, the OTP space seems fixed after it's been made, but is there maybe a similarly unpullable space that CAN be rewritten that could just be axed into oblivion when the PIN counter is exhausted?
that way pulling the firmware couldn't reload an old state with a working PIN counter, although granted, it would still allow potentially resetting of the PIN counter BEFORE it's fully exhausted (aka just try 7 times instead of all eight, reflash the old state, redo).

depending on the speed of this solution this could still take significant time the victim can notice their device being gone, but still not exactly something that seems too awesome.

That is, unless we'd talk about some really hard method that would require kinda 2 regions of code and/or storage

  1. "general"/main storage and "general" code can be read and written as needed, as well as dumped
  2. "secure" code and storage: the code section can only be rewritten by axing the "secure storage" section too and "secure storage" can only be written to or read by secure code, and not dumped (except being written to once, together with the secure code being written, after that no outside access).

that way things like PIN-handling and de/encryption could happen in this "kinda pseudo-smartcard-ish" like area where the retry counter would be observed independently as well as any master keys for decrypting the actual credentials on the general storage.

for obvious reasons the secure code area needs to be as simple strict and bulletproof as possible as you cannot update it without completely throwing out anything secured by it unless you made an update function into itself if secure code can write to itself but usually w^x is a thing and secure update code is also complex AF)

stupid question, I am only really familiar with fido, not really with the microcontrollers, the OTP space seems fixed after it's been made, but is there maybe a similarly unpullable space that CAN be rewritten that could just be axed into oblivion when the PIN counter is exhausted? that way pulling the firmware couldn't reload an old state with a working PIN counter, although granted, it would still allow potentially resetting of the PIN counter BEFORE it's fully exhausted (aka just try 7 times instead of all eight, reflash the old state, redo). depending on the speed of this solution this could still take significant time the victim can notice their device being gone, but still not exactly something that seems too awesome. That is, unless we'd talk about some really hard method that would require kinda 2 regions of code and/or storage 1) "general"/main storage and "general" code can be read and written as needed, as well as dumped 2) "secure" code and storage: the code section can only be rewritten by axing the "secure storage" section too and "secure storage" can only be written to or read by secure code, and not dumped (except being written to once, together with the secure code being written, after that no outside access). that way things like PIN-handling and de/encryption could happen in this "kinda pseudo-smartcard-ish" like area where the retry counter would be observed independently as well as any master keys for decrypting the actual credentials on the general storage. for obvious reasons the secure code area needs to be as simple strict and bulletproof as possible as you cannot update it without completely throwing out anything secured by it unless you made an update function into itself if secure code can write to itself but usually w^x is a thing and secure update code is also complex AF)
polhenarejos commented 2025-09-27 18:10:42 +08:00 (Migrated from github.com)

In this case, yes you @token2 and @My1 are right.

There're DISABLE_BOOTSEL_USB_PICOBOOT_IFC and DISABLE_BOOTSEL_USB_MSD_IFC that would block the access to BOOTSEL mode. However, the device will become unupgradeable at all. It'll lock to this state forever.
DISABLE_BOOTSEL_USB_PICOBOOT_IFC will lock picotool access but the data partition could still be overwrittable through MSD to a skill attacker.

One approach could be create a sideload interface to load new firmware versions, whilst BOOTSEL interface is blocked forever. But this is something it will not happen in a shor time. BTW, the RP2350 board will be locked to Pico Keys project forever.

What do you think.

In this case, yes you @token2 and @My1 are right. There're `DISABLE_BOOTSEL_USB_PICOBOOT_IFC` and `DISABLE_BOOTSEL_USB_MSD_IFC` that would block the access to BOOTSEL mode. However, the device will become unupgradeable at all. It'll lock to this state forever. `DISABLE_BOOTSEL_USB_PICOBOOT_IFC` will lock `picotool` access but the data partition could still be overwrittable through MSD to a skill attacker. One approach could be create a sideload interface to load new firmware versions, whilst `BOOTSEL` interface is blocked forever. But this is something it will not happen in a shor time. BTW, the RP2350 board will be locked to Pico Keys project forever. What do you think.
My1 commented 2025-09-27 18:39:25 +08:00 (Migrated from github.com)

can the firmware on the pico/RP2350 itself remove the disable_bootsel things later on or are these permanent? because a firmware could theoretically make it so it wipes down the data sections, verifies all is clean and then reopen these.

sorry again for asking stupid questions and maybe having ideas that seem nonsensical, as mentioned I really know more about FIDO than any specifics of these microcontrollers.

can the firmware on the pico/RP2350 itself remove the disable_bootsel things later on or are these permanent? because a firmware could theoretically make it so it wipes down the data sections, verifies all is clean and then reopen these. sorry again for asking stupid questions and maybe having ideas that seem nonsensical, as mentioned I really know more about FIDO than any specifics of these microcontrollers.
token2 commented 2025-09-27 18:41:56 +08:00 (Migrated from github.com)

In this case, yes you @token2 and @My1 are right.

There're DISABLE_BOOTSEL_USB_PICOBOOT_IFC and DISABLE_BOOTSEL_USB_MSD_IFC that would block the access to BOOTSEL mode. However, the device will become unupgradeable at all. It'll lock to this state forever. DISABLE_BOOTSEL_USB_PICOBOOT_IFC will lock picotool access but the data partition could still be overwrittable through MSD to a skill attacker.

One approach could be create a sideload interface to load new firmware versions, whilst BOOTSEL interface is blocked forever. But this is something it will not happen in a shor time. BTW, the RP2350 board will be locked to Pico Keys project forever.

What do you think.

Ok, if there is a way to disable the ability to flush the dump at the cost of making the device non-upgradeable, that should ultimately be the user’s decision — it’s a trade-off. However, since you confirmed that PIN brute force is a real attack vector, the code should not allow simple PINs like 1234 (similar to our PIN+ logic). We would actually recommend removing the option entirely and enforcing stronger requirements — for example, a minimum of 12 characters, including at least one lowercase letter, one uppercase letter, one digit, and one special character (similar to default Active Directory password policies).

With that policy, brute-forcing a PIN would take thousands of years, even using the most powerful computing resources available today or in the foreseeable future.

Regarding your last sentence, do you mean that you want to drop support for the RP2040?

> In this case, yes you [@token2](https://github.com/token2) and [@My1](https://github.com/My1) are right. > > There're `DISABLE_BOOTSEL_USB_PICOBOOT_IFC` and `DISABLE_BOOTSEL_USB_MSD_IFC` that would block the access to BOOTSEL mode. However, the device will become unupgradeable at all. It'll lock to this state forever. `DISABLE_BOOTSEL_USB_PICOBOOT_IFC` will lock `picotool` access but the data partition could still be overwrittable through MSD to a skill attacker. > > One approach could be create a sideload interface to load new firmware versions, whilst `BOOTSEL` interface is blocked forever. But this is something it will not happen in a shor time. BTW, the RP2350 board will be locked to Pico Keys project forever. > > What do you think. Ok, if there is a way to disable the ability to flush the dump at the cost of making the device non-upgradeable, that should ultimately be the user’s decision — it’s a trade-off. However, since you confirmed that PIN brute force is a real attack vector, the code should not allow simple PINs like 1234 (similar to our PIN+ logic). We would actually recommend removing the option entirely and enforcing stronger requirements — for example, a minimum of 12 characters, including at least one lowercase letter, one uppercase letter, one digit, and one special character (similar to default Active Directory password policies). With that policy, brute-forcing a PIN would take thousands of years, even using the most powerful computing resources available today or in the foreseeable future. Regarding your last sentence, do you mean that you want to drop support for the RP2040?
My1 commented 2025-09-27 18:48:51 +08:00 (Migrated from github.com)

AD only mentions to use 3 character types rather than all 4 (which one might need to check if that's still enough to be secure in this context, but if it is, would be better as especially special characters are a real pain to enter with different situations like language keyboards, mobile on screen keyboards etc. so 12 chars with 3 types might be less ugly and also not cause the problem where people just add a special character at the end just to satisfy the rules, something even I use from time to time as someone often using wordlist-based passwords.

AD only mentions to use 3 character types rather than all 4 (which one might need to check if that's still enough to be secure in this context, but if it is, would be better as especially special characters are a real pain to enter with different situations like language keyboards, mobile on screen keyboards etc. so 12 chars with 3 types might be less ugly and also not cause the problem where people just add a special character at the end just to satisfy the rules, something even I use from time to time as someone often using wordlist-based passwords.
token2 commented 2025-09-27 19:24:20 +08:00 (Migrated from github.com)

AD only mentions to use 3 character types rather than all 4 (which one might need to check if that's still enough to be secure in this context, but if it is, would be better as especially special characters are a real pain to enter with different situations like language keyboards, mobile on screen keyboards etc. so 12 chars with 3 types might be less ugly and also not cause the problem where people just add a special character at the end just to satisfy the rules, something even I use from time to time as someone often using wordlist-based passwords.

Ok, that would be hundreds of years instead of thousands

> AD only mentions to use 3 character types rather than all 4 (which one might need to check if that's still enough to be secure in this context, but if it is, would be better as especially special characters are a real pain to enter with different situations like language keyboards, mobile on screen keyboards etc. so 12 chars with 3 types might be less ugly and also not cause the problem where people just add a special character at the end just to satisfy the rules, something even I use from time to time as someone often using wordlist-based passwords. Ok, that would be hundreds of years instead of thousands
My1 commented 2025-09-27 22:08:04 +08:00 (Migrated from github.com)

What speed are we even talking about? I'd assume it takes a couple seconds to reflash the thing

Considering dumping and flashing ought to take a couple seconds it can't be that fast.

I tried a simple assumption of a password consisting only of both kinds of letters and numbers, for a total of 62 options per character (ignoring that there are even more possibilities as "3 classes" could also be small, numbers and symbols or small big and symbols) to make the calculation easier.

At around 1000 tries per second (which is absolutely insane considering you have to reflash after 7-8 tries, realistic would maybe between 1-20 per second on average, I'd guess), you'd take around 102 billion years to get through the space completely, that is with the device in hand so ample chance for the loss to be noticed

Cutting down to one type of letters and numbers and a length of 10 (the most likely option for an alnum password under normal) at the same 1000 tries per second we still are at about 115 000 years

What speed are we even talking about? I'd assume it takes a couple seconds to reflash the thing Considering dumping and flashing ought to take a couple seconds it can't be that fast. I tried a simple assumption of a password consisting only of both kinds of letters and numbers, for a total of 62 options per character (ignoring that there are even more possibilities as "3 classes" could also be small, numbers and symbols or small big and symbols) to make the calculation easier. At around 1000 tries per second (which is absolutely insane considering you have to reflash after 7-8 tries, realistic would maybe between 1-20 per second on average, I'd guess), you'd take around 102 billion years to get through the space completely, that is with the device in hand so ample chance for the loss to be noticed Cutting down to one type of letters and numbers and a length of 10 (the most likely option for an alnum password under normal) at the same 1000 tries per second we still are at about 115 000 years
token2 commented 2025-09-27 22:30:44 +08:00 (Migrated from github.com)

What speed are we even talking about? I'd assume it takes a couple seconds to reflash the thing

Considering dumping and flashing ought to take a couple seconds it can't be that fast.

I tried a simple assumption of a password consisting only of both kinds of letters and numbers, for a total of 62 options per character (ignoring that there are even more possibilities as "3 classes" could also be small, numbers and symbols or small big and symbols) to make the calculation easier.

At around 1000 tries per second (which is absolutely insane considering you have to reflash after 7-8 tries, realistic would maybe between 1-20 per second on average, I'd guess), you'd take around 102 billion years to get through the space completely, that is with the device in hand so ample chance for the loss to be noticed

Cutting down to one type of letters and numbers and a length of 10 (the most likely option for an alnum password under normal) at the same 1000 tries per second we still are at about 115 000 years

Correct, it was very simplified. Considering it takes at least 2 minutes to restore from dumped flash, it is a lot more

> What speed are we even talking about? I'd assume it takes a couple seconds to reflash the thing > > Considering dumping and flashing ought to take a couple seconds it can't be that fast. > > I tried a simple assumption of a password consisting only of both kinds of letters and numbers, for a total of 62 options per character (ignoring that there are even more possibilities as "3 classes" could also be small, numbers and symbols or small big and symbols) to make the calculation easier. > > At around 1000 tries per second (which is absolutely insane considering you have to reflash after 7-8 tries, realistic would maybe between 1-20 per second on average, I'd guess), you'd take around 102 billion years to get through the space completely, that is with the device in hand so ample chance for the loss to be noticed > > Cutting down to one type of letters and numbers and a length of 10 (the most likely option for an alnum password under normal) at the same 1000 tries per second we still are at about 115 000 years Correct, it was very simplified. Considering it takes at least 2 minutes to restore from dumped flash, it is a lot more
My1 commented 2025-09-27 22:49:25 +08:00 (Migrated from github.com)

Although assuming there is no code verification, i guess the speed goes up again as one could literally just patch out the function that handles the retry counter.

I hope that otp is at least handled in a way opaque to the fw because if you could flash firmware that reads that and gives it out, that'd be annoying at the least.

Although assuming there is no code verification, i guess the speed goes up again as one could literally just patch out the function that handles the retry counter. I hope that otp is at least handled in a way opaque to the fw because if you could flash firmware that reads that and gives it out, that'd be annoying at the least.
polhenarejos commented 2025-09-28 00:09:17 +08:00 (Migrated from github.com)

I shouldn’t say this but it’s easier than using a physical board. PIN is not encrypted, only hashed. So you could use an offline program to generate the correct hash and compare with the dumped one and check if both match.
With a typical CPU of 5 Mhashes/sec could take like 250 days, less than a year for 8 figures of 3 classes.
Actually the hash of PIN typically does 16 hashes, so this number is a bit higher but you see the point.

I shouldn’t say this but it’s easier than using a physical board. PIN is not encrypted, only hashed. So you could use an offline program to generate the correct hash and compare with the dumped one and check if both match. With a typical CPU of 5 Mhashes/sec could take like 250 days, less than a year for 8 figures of 3 classes. Actually the hash of PIN typically does 16 hashes, so this number is a bit higher but you see the point.
My1 commented 2025-09-28 04:02:28 +08:00 (Migrated from github.com)

I think T2 said that not the PIN itself being encrypted but rather the actual data like key material and stuff is being encrypted by the PIN (+ the OTP data), although I obviously do not know if there is an extra PIN hash stored somewhere and/or if that additionally contains any protections aside from a simple salt attached to the hash (which in terms of a numeric pin would be completely too little, as rainbow tables really arent a problem in a space of 6-8-digit-only PINs.)

now if there's a PIN hash that is unrelated to the encryption key that might need to be changed to similarly take the OTP into account, that is if we even need to store a PIN hash in the first place, as one could just try to decrypt something from data and see if that checks out, I would assume there are checksums in there or AEAD

I think T2 said that not the PIN itself being encrypted but rather the actual data like key material and stuff is being encrypted by the PIN (+ the OTP data), although I obviously do not know if there is an extra PIN hash stored somewhere and/or if that additionally contains any protections aside from a simple salt attached to the hash (which in terms of a numeric pin would be completely too little, as rainbow tables really arent a problem in a space of 6-8-digit-only PINs.) now if there's a PIN hash that is unrelated to the encryption key that might need to be changed to similarly take the OTP into account, that is if we even need to store a PIN hash in the first place, as one could just try to decrypt something from data and see if that checks out, I would assume there are checksums in there or AEAD
My1 commented 2025-10-01 19:54:28 +08:00 (Migrated from github.com)

oh so the PIN actually did have a Hash that did not take the OTP into account, FUN.

good that at least THAT part is solved.

while it obviously doesnt invalidate the issue of being able to bypass the retry counter using a dump, it at the very least is MUCH slower.

but that leaves the question if there are any secure areas that are writable and/or executable that could be taken for a more secure solution for PIN handling.

oh so the PIN actually did have a Hash that did not take the OTP into account, FUN. good that at least THAT part is solved. while it obviously doesnt invalidate the issue of being able to bypass the retry counter using a dump, it at the very least is MUCH slower. but that leaves the question if there are any secure areas that are writable and/or executable that could be taken for a more secure solution for PIN handling.
polhenarejos commented 2025-10-01 20:03:22 +08:00 (Migrated from github.com)

This project started with RP2040, without any measure for protecting data. So, PIN system inherited from this approach. With latest commits, it leverages OTP but now I am switching to a new system where the device key depends on the OTP rather than on PIN. However, it's a bit complex specially for the upgrade.

This project started with RP2040, without any measure for protecting data. So, PIN system inherited from this approach. With latest commits, it leverages OTP but now I am switching to a new system where the device key depends on the OTP rather than on PIN. However, it's a bit complex specially for the upgrade.
My1 commented 2025-10-01 20:08:01 +08:00 (Migrated from github.com)

understandable.

regardless of the device key and encryption, it might be useful to check if there's a way to not be able to restore the PIN counter by flashing an old image.

understandable. regardless of the device key and encryption, it might be useful to check if there's a way to not be able to restore the PIN counter by flashing an old image.
polhenarejos commented 2025-10-01 20:11:49 +08:00 (Migrated from github.com)

The only way is to disable USB BOOTLOADER, which invalidates picotool but also upgrading through normal UF2 procedure.
If you are able to write partitions, then you can always overwrite the PIN counter.

The only way is to disable USB BOOTLOADER, which invalidates `picotool` but also upgrading through normal UF2 procedure. If you are able to write partitions, then you can always overwrite the PIN counter.
token2 commented 2025-10-01 20:20:26 +08:00 (Migrated from github.com)

The only way is to disable USB BOOTLOADER, which invalidates picotool but also upgrading through normal UF2 procedure. If you are able to write partitions, then you can always overwrite the PIN counter.

I would say this is an acceptable workaround (with a trade-off, of course)

> The only way is to disable USB BOOTLOADER, which invalidates `picotool` but also upgrading through normal UF2 procedure. If you are able to write partitions, then you can always overwrite the PIN counter. I would say this is an acceptable workaround (with a trade-off, of course)
My1 commented 2025-10-01 21:09:56 +08:00 (Migrated from github.com)

okay i thought there might be some secure section that cant be as easily read/written from outside (but can from code in itself), that way to pin counter could run protected.

see
https://github.com/polhenarejos/pico-fido/issues/187#issuecomment-3339303596

okay i thought there might be some secure section that cant be as easily read/written from outside (but can from code in itself), that way to pin counter could run protected. see https://github.com/polhenarejos/pico-fido/issues/187#issuecomment-3339303596
sylvainpelissier commented 2025-10-05 16:21:49 +08:00 (Migrated from github.com)

With the new version using the OTP key, the key is hashed with the PIN concatenated several time using SHA-256 int the hash_multi_ext. The construction is not really standard and SHA-256 is vulnerable to Length extension attack. Even if I don't see a direct impact on the current usage, it would be more robust and more efficient to use HMAC-SHA256 since it uses internally only two calls to SHA-256. The updated function would simply calls:

mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), otp_key_1, 32, input, len, output);

I can create a PR with the modification if needed.

With the new version using the OTP key, the key is hashed with the PIN concatenated several time using SHA-256 int the `hash_multi_ext`. The construction is not really standard and SHA-256 is vulnerable to [Length extension attack](https://en.wikipedia.org/wiki/Length_extension_attack). Even if I don't see a direct impact on the current usage, it would be more robust and more efficient to use HMAC-SHA256 since it uses internally only two calls to SHA-256. The updated function would simply calls: ```c mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), otp_key_1, 32, input, len, output); ``` I can create a PR with the modification if needed.
My1 commented 2025-10-05 16:57:51 +08:00 (Migrated from github.com)

if we have a secret to hash the PIN with yeah HMAC would be the way to go if we cant use slow hashes.

if we have a secret to hash the PIN with yeah HMAC would be the way to go if we cant use slow hashes.
sylvainpelissier commented 2025-10-05 17:18:47 +08:00 (Migrated from github.com)

I think we may use PBKDF2-SHA256 algorithm with maybe 1000 iterations but since for boards with OTP keys it would already need a reflash of the board after each 8 trials I think it would be already very slow to bruteforce with HMAC.

I think we may use PBKDF2-SHA256 algorithm with maybe 1000 iterations but since for boards with OTP keys it would already need a reflash of the board after each 8 trials I think it would be already very slow to bruteforce with HMAC.
token2 commented 2025-10-05 17:32:19 +08:00 (Migrated from github.com)

I think we may use PBKDF2-SHA256 algorithm with maybe 1000 iterations but since for boards with OTP keys it would already need a reflash of the board after each 8 trials I think it would be already very slow to bruteforce with HMAC.

Let me clarify: two distinct attack vectors exist. The first is a flash‑dump/physical attack in which the device can be reflashed and an attacker is afforded up to eight PIN attempts per reflash. The second is recovery of the PIN hash from the flash memory followed by offline cracking of that hash. The chosen hash function and parameterization must be sufficiently strong to mitigate both on‑device and offline brute‑force attacks.

> I think we may use PBKDF2-SHA256 algorithm with maybe 1000 iterations but since for boards with OTP keys it would already need a reflash of the board after each 8 trials I think it would be already very slow to bruteforce with HMAC. Let me clarify: two distinct attack vectors exist. The first is a flash‑dump/physical attack in which the device can be reflashed and an attacker is afforded up to eight PIN attempts per reflash. The second is recovery of the PIN hash from the flash memory followed by offline cracking of that hash. The chosen hash function and parameterization must be sufficiently strong to mitigate both on‑device and offline brute‑force attacks.
sylvainpelissier commented 2025-10-05 17:36:32 +08:00 (Migrated from github.com)

I was speaking of the on-device attack, since for offline attack the bruteforce would target the 256-bit OTP key used in the HMAC-SHA256.

I was speaking of the on-device attack, since for offline attack the bruteforce would target the 256-bit OTP key used in the HMAC-SHA256.
token2 commented 2025-10-05 17:48:50 +08:00 (Migrated from github.com)

I was speaking of the on-device attack, since for offline attack the bruteforce would target the 256-bit OTP key used in the HMAC-SHA256.

On-device attack is literally just a slow brute-force, so hashing isn’t playing a role there. We’ve already found a mitigation: disabling BOOTSEL.

> I was speaking of the on-device attack, since for offline attack the bruteforce would target the 256-bit OTP key used in the HMAC-SHA256. On-device attack is literally just a slow brute-force, so hashing isn’t playing a role there. We’ve already found a _mitigation_: disabling BOOTSEL.
polhenarejos commented 2025-10-06 20:28:00 +08:00 (Migrated from github.com)

I changed the flags of the partition table for BL (bootloader, picotool), to "r". It means that the data partition cannot be restored via picotool load. This, jointly with secure boot, avoids these issues. Note that, even with secure boot enabled, it is still possible to load other signed firmwares that potentially modify the partition table to allow write permissions. To avoid this, secure lock is necessary, as it invalidates other firmwares and only allow firmwares signed by me. Perhaps it's a bit restrictive, but I don't see other ways.
Have in mind that, even with non-write permissions, it is still possible to inject backups if the flash memory is desoldered or it's programmed with other methods like SPI, i.e., hard-wiring.

In summary, it's not the magical solution but it puts more complexity to crack the PIN. Even in that case, where an attacker is able to desolder the flash, only a limited attempts are available before reflashing. Note that this is valid only when secure lock is enabled, fobidding the upload of other firmwares not signed by me.

I changed the flags of the partition table for BL (bootloader, picotool), to "r". It means that the `data` partition cannot be restored via `picotool load`. This, jointly with secure boot, avoids these issues. Note that, even with secure boot enabled, it is still possible to load other signed firmwares that potentially modify the partition table to allow `write` permissions. To avoid this, secure lock is necessary, as it invalidates other firmwares and only allow firmwares signed by me. Perhaps it's a bit restrictive, but I don't see other ways. Have in mind that, even with non-write permissions, it is still possible to inject backups if the flash memory is desoldered or it's programmed with other methods like SPI, i.e., hard-wiring. In summary, it's not the magical solution but it puts more complexity to crack the PIN. Even in that case, where an attacker is able to desolder the flash, only a limited attempts are available before reflashing. Note that this is valid only when secure lock is enabled, fobidding the upload of other firmwares not signed by me.
polhenarejos commented 2025-10-08 06:41:19 +08:00 (Migrated from github.com)

A new hierarchical system is introduced in 898c88dc. It is based on key derivation and domain separation depending on the purpose of the derived keys. It's based on HMAC, HKDF and AES-GCM with AAD. It is far robust compared with the previous one.

It doesn't solve the physical image restore we're discussing, as this possibility is always there, but it increases the security formality of the system.

A new hierarchical system is introduced in 898c88dc. It is based on key derivation and domain separation depending on the purpose of the derived keys. It's based on HMAC, HKDF and AES-GCM with AAD. It is far robust compared with the previous one. It doesn't solve the physical image restore we're discussing, as this possibility is always there, but it increases the security formality of the system.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: dearsky/pico-fido#187