Skip to content

Commit 50d2b76

Browse files
committed
Switch from compile-time to install-time parameters
This allows customizing many settings at the time the applet is installed.
1 parent d0cbafc commit 50d2b76

File tree

10 files changed

+257
-59
lines changed

10 files changed

+257
-59
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@ If you want to, feel free! Just raise a pull request or open an issue.
7474

7575
## Where to go Next
7676

77+
If you just want to install the app, look at [what you can configure](docs/installation.md).
78+
7779
I suggest [reading the FAQ](docs/FAQ.md) and perhaps [the security model](docs/security_model.md).
7880

7981
If you're a really detail-oriented person, you might enjoy reading

docs/certs.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,8 @@ certificates (ECDSA) are supported for the authenticator's own certificate; any
1010
may be used for CAs further up the chain.
1111

1212
These may be provided via a vendor CTAP command (command byte 0x46). In order
13-
to enable the vendor CTAP command, you must provide an install parameter of a single byte,
14-
`0x01`. By default, the vendor command will be rejected to avoid inadvertently allowing
15-
an attacker to switch the authenticator to basic attestation mode.
13+
to enable the vendor CTAP command, you must install the applet with parameters enabling it:
14+
see [the install guide](installation.md).
1615

1716
The vendor CTAP command will be rejected if the authenticator already contains a certificate,
1817
or if the authenticator has been used to make any credentials since the last reset, so it must

docs/installation.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Installation Parameters
2+
3+
This applet provides a variety of install-time configurable settings. These are configured via a
4+
CBOR map provided when the applet is installed (for example, via `gpp --install <applet> --params <params>`).
5+
6+
To generate the parameter string, use the `get_install_parameters.py` script at the root of the repository.
7+
The help the script provides (`--help`) explains each options.
8+
9+
The defaults - when no install parameters are provided - are for maximum FIDO standards compatibility, but
10+
won't accept an attestation certificate. So if you want CTAP1/U2F, you'll need to install the applet with
11+
parameters.

docs/security_model.md

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,13 @@ is then AES256-CBC encrypted along with the RP ID, using a random
4242
IV, and the result is used as the "credential ID". Which wrapping key
4343
is used depends on how the credential is generated:
4444

45+
- all credentials use the high security key if `FORCE_ALWAYS_UV`
4546
- credentials created with `credProtect` level 3, "require user
46-
verification for any discovery" always use the high security key
47+
verification for any discovery" always use the high security key,
48+
unless `LOW_SECURITY_MAXIMUM_COMPATIBILITY` is set
4749
- credentials stored on the authenticator itself ("discoverable") use
48-
the high security if the tunable `USE_LOW_SECURITY_FOR_SOME_RKS` is
49-
false
50+
the high security key if the tunable `USE_LOW_SECURITY_FOR_SOME_RKS`
51+
is false
5052
- other credentials (non-discoverable, `credProtect` level zero
5153
through two) use the low security key
5254

@@ -237,6 +239,8 @@ Open source is open.
237239

238240
## That's all too complicated! Show me a table!
239241

242+
The below table applies with `FORCE_ALWAYS_UV` and `LOW_SECURITY_HIGH_COMPATIBILITY` disabled.
243+
240244
| Usage / Creation | L1D | L2D | L3D | L1 | L2 | L3 |
241245
|---------------------------------------|---------|----------|----------|----|----|----------|
242246
| Provided, no PIN set | OK | OK | Software | OK | OK | Software |

get_install_parameters.py

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
#!/usr/bin/env python
2+
3+
import argparse
4+
5+
if __name__ == '__main__':
6+
parser = argparse.ArgumentParser("get_install_parameters",
7+
description="Return parameters for installing FIDO2Applet with custom settings")
8+
parser.add_argument('--enable-attestation', action='store_true', default=None,
9+
help="Allows loading an attestation certificate after installing the applet")
10+
parser.add_argument('--high-security', action='store_true', default=None,
11+
help="Does not comply with the FIDO standards, but protects credentials against bugs or "
12+
"faulty authenticator hardware. Implies high-security RKs.")
13+
parser.add_argument('--force-always-uv', action='store_true', default=None,
14+
help="Requires the PIN for all operations, always")
15+
parser.add_argument('--high-security-rks', action='store_true', default=None,
16+
help="Protects discoverable credentials against bugs and faulty authenticator hardware,"
17+
"at the cost of standards compliance")
18+
parser.add_argument('--protect-against-reset', action='store_true', default=None,
19+
help="Require sending a reset command twice, across two power cycles, to truly reset "
20+
"the authenticator")
21+
parser.add_argument('--kdf-iterations', type=int, default=5,
22+
help="Number of iterations of the Key Derivation Function used. Protects against "
23+
"brute-force attacks against the PIN (when authenticator hardware is faulty), "
24+
"at the cost of performance")
25+
parser.add_argument('--max-cred-blob-len', type=int, default=32,
26+
help="Maximum length of the blob stored with every discoverable credential. Must be >=32")
27+
parser.add_argument('--large-blob-store-size', type=int, default=1024,
28+
help="Length of the large blob array in flash memory. Must be >=1024")
29+
parser.add_argument('--max-rk-rp-length', type=int, default=32,
30+
help="Number of bytes of the relying party identifier stored with each RK. Must be >=32")
31+
parser.add_argument('--max-ram-scratch', type=int, default=254,
32+
help="Number of bytes of RAM to use for working memory. Reduces flash wear. Must be <=254")
33+
parser.add_argument('--buffer-mem', type=int, default=1024,
34+
help="Number of bytes of RAM to use for request processing. Reduces flash wear. Must be >=1024")
35+
parser.add_argument('--flash-scratch', type=int, default=1024,
36+
help="Number of bytes of flash to use when RAM is exhausted. For low-memory situations")
37+
38+
args = parser.parse_args()
39+
40+
num_options_set = 0
41+
install_param_bytes = []
42+
for option_number, option_string in enumerate([
43+
'enable_attestation',
44+
'high_security',
45+
'force_always_uv',
46+
'high_security_rks',
47+
'protect_against_reset',
48+
'kdf_iterations',
49+
'max_cred_blob_len',
50+
'large_blob_store_size',
51+
'max_rk_rp_length',
52+
'max_ram_scratch',
53+
'buffer_mem',
54+
'flash_scratch'
55+
]):
56+
val = getattr(args, option_string)
57+
if val is None:
58+
continue
59+
num_options_set += 1
60+
61+
bytes_for_option = []
62+
if val is True:
63+
bytes_for_option = [0xF5]
64+
elif val is False:
65+
bytes_for_option = [0xF4]
66+
else:
67+
if val <= 23:
68+
bytes_for_option = [val]
69+
elif val <= 255:
70+
bytes_for_option = [0x18, val]
71+
else:
72+
bytes_for_option = [0x19, (val & 0xFF00) >> 8, val & 0x00FF]
73+
74+
install_param_bytes += [option_number] + bytes_for_option
75+
76+
install_param_bytes = [0xA0 + num_options_set] + install_param_bytes
77+
print(bytes(install_param_bytes).hex())

python_tests/ctap/ctap_test.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ def setUp(self, install_params: Optional[bytes] = None) -> None:
221221
],
222222
}
223223
if install_params is None:
224-
install_params = bytes([0x01])
224+
install_params = bytes([0xA1, 0x00, 0xF5])
225225
super().setUp(install_params)
226226
if self.PCSC_MODE:
227227
devs = list(CtapPcscDevice.list_devices(self.VIRTUAL_DEVICE_NAME))

python_tests/ctap/test_ctap_attestation_mode_switch.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
class AttestationModeSwitchTestCase(BasicAttestationTestCase):
1212
def setUp(self, install_params: Optional[bytes] = None) -> None:
13-
super().setUp(bytes([0x01]))
13+
super().setUp(bytes([0xA1, 0x00, 0xF5]))
1414

1515
@parameterized.expand([
1616
("tiny", 3),

python_tests/ctap/test_extended_apdus.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ def setUpClass(cls) -> None:
1515
super().setUpClass()
1616

1717
def setUp(self, install_params: Optional[bytes] = None) -> None:
18-
super().setUp(bytes([0x01]))
18+
super().setUp(bytes([0xA1, 0x00, 0xF5]))
1919

2020
def test_get_info(self):
2121
info = self.ctap2.get_info()

0 commit comments

Comments
 (0)