diff --git a/README.md b/README.md index 6e1d37c31..ca9269053 100644 --- a/README.md +++ b/README.md @@ -30,11 +30,13 @@ Secrets App supports the following features: - Touch-button protected use per credential. The pynitrokey library can be used to communicate with this application over CTAPHID, and nitropy provides the CLI using -it. +it. See [ctaphid.md](docs/ctaphid.md) for the details. CCID transport is also available, and while not supported in the mentioned library yet, it can be potentially used by the protocol-compatible applications, like the mentioned KeepassXC. +See [design.md](docs/design.md) for the UX design choices. + [RFC4226]: https://www.rfc-editor.org/rfc/rfc4226 [RFC6238]: https://www.rfc-editor.org/rfc/rfc6238 @@ -99,7 +101,7 @@ e.g. due to being taken by other services, or requiring Administrator privileges). A CTAPHID vendor command number was selected to use (`0x70`), thus allowing for a compatible extension of any FIDO compliant device. -See [CTAPHID](ctaphid.md) for the further documentation regarding the NLnet funded CTAPHID extension. +See [CTAPHID](docs/ctaphid.md) for the further documentation regarding the NLnet funded CTAPHID extension. ### Further work @@ -119,6 +121,8 @@ Tasks and features still discussed to be done: ### Development +See [design](docs/design.md) document to see decisions taken to make the solution cohesive. + Use `dangerous_disable_encryption` Rust flag to disable data encryption for the debug purposes. E.g.: ```text diff --git a/ctaphid.md b/ctaphid.md deleted file mode 100644 index f2fc1d14a..000000000 --- a/ctaphid.md +++ /dev/null @@ -1,265 +0,0 @@ - - -# Oath Authenticator Client - -The [Oath Authenticator] application has been chosen as a good candidate due to being written in an extensive way, and -offered in the same language as the platform, thus guaranteeing high compatibility and maintainability. - -It offers HOTP and TOTP implementations ([RFC4226] and [RFC6238] respectively), with SHA1 and SHA256 hashes support. It -manages to process 320+ bits of the shared key. - -The protocol it uses - [YKOATH] - is using [ISO7816-4] commands for communication. - -[RFC6238]: https://www.rfc-editor.org/rfc/rfc6238 - -[Oath Authenticator]: https://github.com/trussed-dev/oath-authenticator - -[YKOATH]: https://developers.yubico.com/OATH/YKOATH_Protocol.html - -## Protocol Description - -This implementation uses CTAPHID to transfer commands to the Oath Authenticator application, compiled into the Nitrokey -3 firmware. This transport was used to improve compatibility on platforms, where the default transport for this -application, CCID, is not easily available (e.g. due to being taken by other services, or requiring Administrator -privileges). In CTAPHID, a custom vendor command number was selected `0x70`, thus allowing for a compatible extension of -any FIDO device. - -Below is a visualization of getting the OTP code from the device. First the ISO7816 message is created and encapsulated -into CTAPHID message, which is unpacked on the device and passed further to the Oath Authenticator. Once parsed and -processed, the response is produced, which traverses the same way backwards, finally reaching Python client over -CTAPHID. - - -```mermaid -sequenceDiagram - Python Client ->> FIDO2 device : CTAPHID message - FIDO2 device ->> Oath Authenticator : ISO7816 message - Oath Authenticator -->> FIDO2 device : Response - FIDO2 device -->> Python Client : Response -``` - -Following commands are accepted by the Oath Authenticator: - -| Command | Cls | Ins | P1 | P2 | Description | -|-----------|------|------|--------|------|------------------------------------------| -| Put | 0x00 | 0x01 | 0x00 | 0x00 | Register a new OTP credential | -| Delete | 0x00 | 0x02 | 0x00 | 0x00 | Delete a registered OTP credential | -| Reset | 0x00 | 0x04 | 0xDE | 0xAD | Remove all stored OTP credentials | -| List | 0x00 | 0xA1 | 0x00 | 0x00 | List stored OTP credentials | -| Calculate | 0x00 | 0xA2 | 0x00 | 0x01 | Calculate an OTP code for the credential | - -This is a standard ISO7816 encoding of the command and its parameters. The P1 and P2 are mostly unused, except for the -case of Reset and Calculate commands. The class `cls` parameter is always `0`. - -Same table, but graphically: - -![Command format](images/command_format.png "Command format") - - - -## Commands - -### Put - -#### Input - -| Command | Cls | Ins | P1 | P2 | Description | -|-----------|------|------|--------|------|------------------------------------------| -| Put | 0x00 | 0x01 | 0x00 | 0x00 | Register a new OTP credential | - -| Parameters | Type | Description | -|----------------|--------|--------------------------------------------------------------------------------------------| -| CredentialId | Bytes | The credential name, stored for the later reference and listing | -| Key | Bytes | The shared key in raw bytes | -| Type* | u8 | OtpKind "bitwiseOr" Hash algorithm. Values are described below. Prefixed to the Key field. | -| Digits* | u8 | Digits count. The common values are `6` and `8`. Prefixed to the Key field. | -| InitialCounter | u32 BE | Initial value for the HOTP counter, encoded in big endian. | - -Fields marked with `*` are concatenated with the `Key` field. - -| Tag | Value | Description | -|----------------|-------|---------------------------------------------------------------------------------------------| -| CredentialId | 0x71 | The credential name, stored for the later reference and listing | -| Key | 0x73 | \[ OtpKind bitwiseOr HashAlgorithm, digits, shared key \] | -| Challenge | 0x74 | The challenge value for the TOTP calculations. 64-bit unsigned integer, big endian encoded. | -| InitialCounter | 0x7A | Initial value for the HOTP counter. 32-bit unsigned integer, big endian encoded. | - -| Kind | Value | Description | -|--------------|-------|-----------------------------------------------------------| -| HOTP | 0x10 | Calculate OTP as HOTP, against the internal counter | -| TOTP | 0x20 | Calculate OTP as TOTP, against the provided challenge | -| REVERSE_HOTP | 0x30 | Calculate HOTP code, and compare against the provided one | - -| Algorithm | Value | Description | -|-----------|-------|---------------------------| -| Sha1 | 0x01 | Use SHA1 hash algorithm | -| Sha256 | 0x02 | Use SHA256 hash algorithm | - -#### Response - -None - -### Calculate - -#### Input - -| Command | Cls | Ins | P1 | P2 | Description | -|-----------|------|------|--------|------|------------------------------------------| -| Calculate | 0x00 | 0xA2 | 0x00 | 0x01 | Calculate an OTP code for the credential | - -| Parameters | Type | Description | -|--------------|--------|-----------------------------------------------------------------| -| CredentialId | Bytes | The credential name, stored for the later reference and listing | -| Challenge | u64 BE | The challenge value to run calculation against | - -Fields marked with `*` are concatenated with the `Key` field. - -#### Response - -![The result of the Get Code command](images/get_code_result.png "Get code result") - - -The received digest is calculated according to the [RFC4226] specification. This means it has to be processed further to -obtain the target OTP code as follows: - -```python -# digits - user provided value during registration for selecting the final code length -# digest - calculated hash some, received from the device -truncated_code = int.from_bytes(digest, byteorder="big", signed=False) -code = (truncated_code & 0x7FFFFFFF) % pow(10, digits) -code_string = str(code).zfill(digits) -``` - -[RFC4226]: https://www.rfc-editor.org/rfc/rfc4226 - -[ctap-vendor]: https://fidoalliance.org/specs/fido-v2.0-id-20180227/fido-client-to-authenticator-protocol-v2.0-id-20180227.html#usb-vendor-specific-commands - -[ISO7816]: https://www.iso.org/standard/54550.html - -[ISO7816-4]: https://www.iso.org/standard/54550.html - -### List - -List command returns a TLV encoded list of binary strings: - - -![Credetials list](images/credentials.png "Credentials") - - - -### Delete - -| Command | Cls | Ins | P1 | P2 | Description | -|-----------|------|------|--------|------|------------------------------------------| -| Delete | 0x00 | 0x02 | 0x00 | 0x00 | Delete a registered OTP credential | - -#### Input - -| Parameters | Type | Description | -|--------------|-------|-----------------------------------------------------------------| -| CredentialId | Bytes | The credential name, stored for the later reference and listing | - -#### Response - -None - -## Tests - -The tests for communication and responses correctness according to the RFC test vectors are provided in `test_otp.py` -file. - -These can be run against a USB/IP device simulation of Nitrokey 3. - -## Client Application - -The Oath Authenticator can be reached through the described protocol over a pynitrokey CLI experimental interface. Excerpt from its help screen follows: - -```text -$ nitropy nk3 otp -Command line tool to interact with Nitrokey devices 0.4.30 -Usage: nitropy nk3 otp [OPTIONS] COMMAND [ARGS]... - - Manage OTP secrets on the device. - -Options: - --help Show this message and exit. - -Commands: - get Generate OTP code from registered credential. - register Register OTP credential. - remove Remove OTP credential. - reset Remove all OTP credentials from the device. - show List registered OTP credentials. - -$ nitropy nk3 otp register --help -Command line tool to interact with Nitrokey devices 0.4.30 -Usage: nitropy nk3 otp register [OPTIONS] NAME SECRET - - Register OTP credential. - - Write SECRET under the NAME. SECRET should be encoded in base32 format. - -Options: - --digits_str [6|8] Digits count - --kind [TOTP|HOTP] OTP mechanism to use - --hash [SHA1|SHA256] Hash algorithm to use - --counter_start INTEGER Starting value for the counter (HOTP only) - --help Show this message and exit. - -$ nitropy nk3 otp get --help -Command line tool to interact with Nitrokey devices 0.4.30 -Usage: nitropy nk3 otp get [OPTIONS] NAME - - Generate OTP code from registered credential. - -Options: - --timestamp INTEGER The timestamp to use instead of the local time (TOTP - only) - --period INTEGER The period to use in seconds (TOTP only) - --help Show this message and exit. -``` - - -## Authentication Requirements - - -| Command | Require Touch Button press | Require PIN Authentication | Unlocks PIN-based Encryption Key | -|-----------------|----------------------------|----------------------------|----------------------------------| -| Select | No | No | No | -| Calculate | Yes* | No | No | -| CalculateAll | No | No | No | -| Delete | Yes | Yes* | No | -| ListCredentials | No | Yes* | No | -| Register | Yes | Yes* | No | -| Reset | Yes | No | No | -| VerifyPin | No | - | Yes | -| SetPin | Yes | - | No | -| ChangePin | Yes | - | No | -| VerifyCode | Yes* | No | No | -| SendRemaining | No | No | No | - - -Notes: -1. \* If credential was encrypted using PIN-based key, then verification with PIN through `VerifyPin()` is required to get -access to it, otherwise it will be reported as not found. Similarly, PIN-encrypted credentials will not be listed -with `List` command until authenticated. `Delete` command require PIN for Credentials created with PIN-encryption. -2. Credentials can be configured to be allowed for use only after touch button is pressed. -3. \* Touch Button press is required for Credentials, which were created with such attribute. - -## Further development - -Current solution does have a couple of limitations, which could be corrected in the further development: - -- initial HOTP counter value can't be bigger than 2^32; -- SHA512 algorithm is not supported at the moment. - -Both limitations are not affecting the daily usage. - - - -## Funding - -[Logo NLnet: abstract logo of four people seen from above](https://nlnet.nl/) -[Logo NGI Zero: letterlogo shaped like a tag](https://nlnet.nl/NGI0/) - -Changes in this project were funded through the [NGI0 PET](https://nlnet.nl/PET) Fund, a fund established by [NLnet](https://nlnet.nl/) with financial support from the European Commission's [Next Generation Internet programme](https://ngi.eu/), under the aegis of DG Communications Networks, Content and Technology under grant agreement No 825310. diff --git a/docs/ctaphid.md b/docs/ctaphid.md new file mode 100644 index 000000000..489be0a64 --- /dev/null +++ b/docs/ctaphid.md @@ -0,0 +1,317 @@ +# Secrets App CTAPHID Protocol Details + +## Protocol Description + +This implementation uses CTAPHID to transfer commands to the Secrets App application, compiled into the Nitrokey +3 firmware. This transport was used to improve compatibility on platforms, where the default transport for this +application, CCID, is not easily available (e.g. due to being taken by other services, or requiring Administrator +privileges). In CTAPHID, a custom vendor command number was selected `0x70`, thus allowing for a compatible extension of +any FIDO device. + +Below is a visualization of getting the OTP code from the device. First the ISO7816 message is created and encapsulated +into CTAPHID message, which is unpacked on the device and passed further to the Secrets App. Once parsed and +processed, the response is produced, which traverses the same way backwards, finally reaching Python client over +CTAPHID. + + + +```mermaid +sequenceDiagram + Python Client ->> FIDO2 device : CTAPHID message + FIDO2 device ->> Secrets App : ISO7816 message + Secrets App -->> FIDO2 device : Response + FIDO2 device -->> Python Client : Response +``` + +Following commands are accepted by the Secrets App: + +| Command | Cls | Ins | P1 | P2 | Description | +|------------------|------|------|------|------|-----------------------------------------------------------------| +| Put | 0x00 | 0x01 | 0x00 | 0x00 | Register a new OTP credential | +| Delete | 0x00 | 0x02 | 0x00 | 0x00 | Delete a registered OTP credential | +| Reset | 0x00 | 0x04 | 0xDE | 0xAD | Remove all stored OTP credentials | +| List | 0x00 | 0xA1 | 0x00 | 0x00 | List stored OTP credentials | +| Calculate | 0x00 | 0xA2 | 0x00 | 0x01 | Calculate an OTP code for the credential | +| VerifyCode | 0x00 | 0xB1 | 0x00 | 0x00 | Reverse HOTP - verify incoming HOTP code | +| VerifyPIN | 0x00 | 0xB2 | 0x00 | 0x00 | Authenticate with provided PIN | +| ChangePIN | 0x00 | 0xB3 | 0x00 | 0x00 | Change PIN | +| SetPIN | 0x00 | 0xB4 | 0x00 | 0x00 | Set PIN. Can be called only once, directly after factory reset. | +| GetCredential | 0x00 | 0xB5 | 0x00 | 0x00 | Get static password entry | +| CredentialUpdate | 0x00 | 0xB7 | 0x00 | 0x00 | Update static password entry | + +This is a standard ISO7816 encoding of the command and its parameters. The P1 and P2 are mostly unused, except for the +case of Reset and Calculate commands. The class `cls` parameter is always `0`. + +Presenting graphically different variants for each field (selected commands) : + +![Command format](images/command_format.png "Command format") + +## Commands + +Chosen commands description follows: + +### Put + +#### Input + +| Command | Cls | Ins | P1 | P2 | Description | +|---------|------|------|------|------|-------------------------------| +| Put | 0x00 | 0x01 | 0x00 | 0x00 | Register a new OTP credential | + +| Parameters | Type | Description | +|----------------|--------|--------------------------------------------------------------------------------------------| +| CredentialId | Bytes | The credential name, stored for the later reference and listing | +| Key | Bytes | The shared key in raw bytes | +| Type* | u8 | OtpKind "bitwiseOr" Hash algorithm. Values are described below. Prefixed to the Key field. | +| Digits* | u8 | Digits count. The common values are `6` and `8`. Prefixed to the Key field. | +| InitialCounter | u32 BE | Initial value for the HOTP counter, encoded in big endian. | +| PwsLogin | Bytes | Value for the Password Safe entry - login field | +| PwsPassword | Bytes | Value for the Password Safe entry - password field | +| PwsMetadata | Bytes | Value for the Password Safe entry - metadata field | + +Fields marked with `*` are concatenated with the `Key` field. + +| Tag | Value | Description | +|----------------|-------|---------------------------------------------------------------------------------------------| +| CredentialId | 0x71 | The credential name, stored for the later reference and listing | +| Key | 0x73 | \[ OtpKind bitwiseOr HashAlgorithm, digits, shared key \] | +| Challenge | 0x74 | The challenge value for the TOTP calculations. 64-bit unsigned integer, big endian encoded. | +| InitialCounter | 0x7A | Initial value for the HOTP counter. 32-bit unsigned integer, big endian encoded. | +| PwsLogin | 0x83 | Value for the Password Safe entry - login field | +| PwsPassword | 0x84 | Value for the Password Safe entry - password field | +| PwsMetadata | 0x85 | Value for the Password Safe entry - metadata field | + +| Kind | Value | Description | +|--------------|-------|-----------------------------------------------------------| +| HOTP | 0x10 | Calculate OTP as HOTP, against the internal counter | +| TOTP | 0x20 | Calculate OTP as TOTP, against the provided challenge | +| REVERSE_HOTP | 0x30 | Calculate HOTP code, and compare against the provided one | +| HMAC | 0x40 | Calculate HMAC-challenge value | + +| Algorithm | Value | Description | +|-----------|-------|---------------------------| +| Sha1 | 0x01 | Use SHA1 hash algorithm | +| Sha256 | 0x02 | Use SHA256 hash algorithm | + +#### Response + +None + +### Calculate + +#### Input + +| Command | Cls | Ins | P1 | P2 | Description | +|-----------|------|------|------|------|------------------------------------------| +| Calculate | 0x00 | 0xA2 | 0x00 | 0x01 | Calculate an OTP code for the credential | + +| Parameters | Type | Description | +|--------------|--------|-----------------------------------------------------------------| +| CredentialId | Bytes | The credential name, stored for the later reference and listing | +| Challenge | u64 BE | The challenge value to run calculation against | + +Fields marked with `*` are concatenated with the `Key` field. + +#### Response + +![The result of the Get Code command](images/get_code_result.png "Get code result") + +The received digest is calculated according to the [RFC4226] specification. This means it has to be processed further to +obtain the target OTP code as follows: + +```python +# digits - user provided value during registration for selecting the final code length +# digest - calculated hash some, received from the device +truncated_code = int.from_bytes(digest, byteorder="big", signed=False) +code = (truncated_code & 0x7FFFFFFF) % pow(10, digits) +code_string = str(code).zfill(digits) +``` + +[RFC4226]: https://www.rfc-editor.org/rfc/rfc4226 + +[ctap-vendor]: https://fidoalliance.org/specs/fido-v2.0-id-20180227/fido-client-to-authenticator-protocol-v2.0-id-20180227.html#usb-vendor-specific-commands + +[ISO7816]: https://www.iso.org/standard/54550.html + +[ISO7816-4]: https://www.iso.org/standard/54550.html + +### List + +List command returns a TLV encoded list of binary strings (version 1 format): + +![Credetials list](images/credentials.png "Credentials") + +### Delete + +| Command | Cls | Ins | P1 | P2 | Description | +|---------|------|------|------|------|------------------------------------| +| Delete | 0x00 | 0x02 | 0x00 | 0x00 | Delete a registered OTP credential | + +#### Input + +| Parameters | Type | Description | +|--------------|-------|-----------------------------------------------------------------| +| CredentialId | Bytes | The credential name, stored for the later reference and listing | + +#### Response + +None + +### CredentialUpdate + +| Command | Cls | Ins | P1 | P2 | Description | +|------------------|------|------|------|------|------------------------------| +| CredentialUpdate | 0x00 | 0xB7 | 0x00 | 0x00 | Update static password entry | + + +#### Input + +| Parameters | Type | Description | +|--------------|-------|-----------------------------------------------------------------| +| CredentialId | Bytes | The credential name, stored for the later reference and listing | +| NewName | Bytes | The credential new name | +| PwsLogin | Bytes | Value for the Password Safe entry - login field | +| PwsPassword | Bytes | Value for the Password Safe entry - password field | +| PwsMetadata | Bytes | Value for the Password Safe entry - metadata field | + + +| Tag | Value | Description | +|--------------|-------|-----------------------------------------------------------------| +| CredentialId | 0x71 | The credential name, stored for the later reference and listing | +| NewName | 0x71 | The credential new name. Uses same tag id as the previous field | +| PwsLogin | 0x83 | Value for the Password Safe entry - login field | +| PwsPassword | 0x84 | Value for the Password Safe entry - password field | +| PwsMetadata | 0x85 | Value for the Password Safe entry - metadata field | + +#### Response + +None + +## Tests + +The tests for communication and responses correctness according to the RFC test vectors are provided in `test_secrets_app.py` +file, located in the pynitrokey repository: +- https://github.com/Nitrokey/pynitrokey/blob/master/pynitrokey/test_secrets_app.py + +These can be run against a USB/IP device simulation of Nitrokey 3. + +## Client Application + +The Secrets App can be reached through the described protocol over a pynitrokey CLI experimental interface. Excerpt from +its help screen follows: + +```text +$ nitropy nk3 secrets +Command line tool to interact with Nitrokey devices 0.4.39 +Usage: nitropy nk3 secrets [OPTIONS] COMMAND [ARGS]... + + Nitrokey Secrets App. Manage OTP and Password Safe secrets on the device. + Use NITROPY_SECRETS_PASSWORD to pass password for the scripted execution. + +Options: + --help Show this message and exit. + +Commands: + add-challenge-response Register Challenge-Response credential. + add-otp (register) Register OTP credential. + add-password Register Password Safe credential. + get-otp (get) Generate OTP code from registered credential. + get-password Get Password Safe Entry + list List registered OTP credentials. + remove Remove OTP credential. + rename Rename credential. + reset Remove all OTP credentials from the device. + set-pin Set or change the PIN used to authenticate to... + status Show application status + update Update credential. + verify Proceed with the incoming OTP code verification... +$ nitropy nk3 secrets register --help +Command line tool to interact with Nitrokey devices 0.4.39 +Usage: nitropy nk3 secrets register [OPTIONS] NAME SECRET + + Register OTP credential. + + Write credential under the NAME. Secret should be base32 encoded. + +Options: + --digits-str [6|8] Digits count + --kind [HOTP|TOTP|HOTP_REVERSE|HMAC] + OTP mechanism to use. Case insensitive. + --hash [SHA1|SHA256] Hash algorithm to use + --counter-start INTEGER Starting value for the counter (HOTP only) + --touch-button This credential requires button press before + use + --protect-with-pin This credential should be additionally + encrypted with a PIN, which will be required + before each use + --help Show this message and exit. +$ nitropy nk3 secrets get-otp --help +Command line tool to interact with Nitrokey devices 0.4.39 +Usage: nitropy nk3 secrets get-otp [OPTIONS] NAME + + Generate OTP code from registered credential. + +Options: + --timestamp INTEGER The timestamp to use instead of the local time (TOTP + only) + --period INTEGER The period to use in seconds (TOTP only) + --help Show this message and exit. +$ nitropy nk3 secrets get-password --help +Command line tool to interact with Nitrokey devices 0.4.39 +Usage: nitropy nk3 secrets get-password [OPTIONS] NAME + + Get Password Safe Entry + +Options: + --password Print password only + --format [json|csv] Format of the output + --help Show this message and exit. + +``` + +## Authentication Requirements + +| Command | Require Touch Button press | Require PIN Authentication | Unlocks PIN-based Encryption Key | +|------------------|----------------------------|----------------------------|----------------------------------| +| Select | No | No | No | +| Calculate | Yes* | No | No | +| Delete | Yes | Yes* | No | +| ListCredentials | No | Yes* | No | +| Register | Yes | Yes* | No | +| Reset | Yes | No | No | +| VerifyPin | No | - | Yes | +| SetPin | Yes | - | No | +| ChangePin | Yes | - | No | +| VerifyCode | Yes* | No | No | +| SendRemaining | No | No | No | +| GetCredential | Yes* | No | No | +| CredentialUpdate | Yes | Yes* | No | + +Notes: + +1. \* If credential was encrypted using PIN-based key, then verification with PIN through `VerifyPin()` is required to + get + access to it, otherwise it will be reported as not found. Similarly, PIN-encrypted credentials will not be listed + with `List` command until authenticated. `Delete` command require PIN for Credentials created with PIN-encryption. +2. Credentials can be configured to be allowed for use only after touch button is pressed. +3. \* Touch Button press is required for Credentials, which were created with such attribute, + but omitted if the PIN was already verified + +## Further development + +Current solution does have a couple of limitations, which could be corrected in the further development: + +- initial HOTP counter value can't be bigger than 2^32; +- SHA512 algorithm is not supported at the moment. + +Both limitations are not affecting the daily usage. + +## Funding + +[Logo NLnet: abstract logo of four people seen from above](https://nlnet.nl/) +[Logo NGI Zero: letterlogo shaped like a tag](https://nlnet.nl/NGI0/) + +Changes in this project were funded through the [NGI0 PET](https://nlnet.nl/PET) Fund, a fund established +by [NLnet](https://nlnet.nl/) with financial support from the European +Commission's [Next Generation Internet programme](https://ngi.eu/), under the aegis of DG Communications Networks, +Content and Technology under grant agreement No 825310. diff --git a/docs/design.md b/docs/design.md new file mode 100644 index 000000000..ee59689a5 --- /dev/null +++ b/docs/design.md @@ -0,0 +1,29 @@ +# Design + +The following design choices have been implemented to strike a balance between the secure utilization and +user-friendliness of the Secrets App project. These decisions originated from the [YKOATH] protocol and were compared with +alternative offline solutions. Throughout this process, the aim was to ensure a basic level of safeguarding against +malware threats by incorporating physical user presence confirmation for critical operations. + +[YKOATH]: https://developers.yubico.com/OATH/YKOATH_Protocol.html + +| | 1\. Daily use | 2\. Registration / modification | 3\. Factory Reset confirmation | 4\. PINs / passphrases | 5\. PIN change guard | 6\. Attack vector protection | 7\. Token validation period | +|------------------------|-------------------------------------------------------------|---------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------|------------------------|------------------------------|------------------------------|-------------------------------------------------| +| Secrets App v0.10+ | Touch button if set, and/or
PIN if set on the Credential | Touch button always, before processing (to prevent PIN attempt counter use up). | Touch button | Single PIN only | Current PIN and Touch button | Local / Malware | Each request, where PIN is needed (per request) | +| Secrets App Next (TBD) | (no changes) | (no changes) | \- Touch button
\- Within 10 seconds of power cycle only
\- Significant UX event – LED animation red/blue blinking | (no changes) | (no changes) | (no changes) | (no changes) | + + + +| | Comments | +|------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| Secrets App v0.10+ | Do not require PIN for user data, but offer such possibility. Keep all encrypted. PIN-encrypted Credentials are not listed until PIN is provided. Touch button should always protect PIN use to prevent local malware DOS attack. | +| Secrets App Next (TBD) | (no changes) | + + +Other: +- do not allow to overwrite credentials - always require explicit deletion of the credential with the same name +- remove YKOATH protocol compatibility, specifically authentication through challenge-response + +Privacy concerns: +- the credential overwrite protection (for the Update and Register commands) can leak information about the presence of a PIN-encrypted credential with the same name. See below ticket for details: + - https://github.com/Nitrokey/trussed-secrets-app/issues/101 \ No newline at end of file diff --git a/src/authenticator.rs b/src/authenticator.rs index 13d37b1a0..dfc694f39 100644 --- a/src/authenticator.rs +++ b/src/authenticator.rs @@ -283,7 +283,7 @@ where self.state.runtime.previously = None; } - // DESIGN Allow all commands to be called without PIN verification + // DESIGN (see design.md): Allow all commands to be called without PIN verification // Lazy init: make sure hardware key is initialized self.init()?; @@ -315,7 +315,7 @@ where }; // Call logout after processing, so the PIN-based KEK would not be kept in the memory - // DESIGN -> Per-request authorization + // DESIGN (see design.md): -> Per-request authorization if self.state.runtime.encryption_key.is_some() { // Do not call automatic logout after these commands match command { @@ -384,7 +384,7 @@ where } fn reset(&mut self) -> Result { - // DESIGN Reset: always confirm with touch button + // DESIGN (see design.md): Reset: always confirm with touch button self.user_present()?; // Run any structured cleanup we have @@ -598,7 +598,7 @@ where } fn register(&mut self, register: command::Register<'_>) -> Result { - // DESIGN Registration: require touch button if set on the credential, but not if the PIN was already checked + // DESIGN (see design.md): Registration: require touch button if set on the credential, but not if the PIN was already checked if register.credential.touch_required && register.credential.encryption_key_type != EncryptionKeyType::PinBased { @@ -794,10 +794,10 @@ where update_req: command::UpdateCredential<'_>, _reply: &mut Data, ) -> Result { - // DESIGN Get operation confirmation from user before proceeding + // DESIGN (see design.md): Get operation confirmation from user before proceeding self.user_present()?; - // DESIGN check if the target name is occupied already + // DESIGN (see design.md): check if the target name is occupied already if let Some(new_label) = update_req.new_label { self.err_if_credential_with_label_exists(new_label)?; } @@ -852,7 +852,7 @@ where } fn require_touch_if_needed(&mut self, credential: &CredentialFlat) -> Result<()> { - // DESIGN Daily use: require touch button if set on the credential, but not if the PIN was already checked + // DESIGN (see design.md): Daily use: require touch button if set on the credential, but not if the PIN was already checked // Safety: encryption_key_type should be set for credential during loading in load_credential if credential.touch_required && credential.encryption_key_type.unwrap() != EncryptionKeyType::PinBased @@ -1159,7 +1159,7 @@ where self._extension_logout()?; - // DESIGN Always ask for touch button confirmation before verifying PIN, to prevent + // DESIGN (see design.md): Always ask for touch button confirmation before verifying PIN, to prevent // non-intentional attempt counter use up self.user_present()?; @@ -1179,7 +1179,7 @@ where if self._extension_is_pin_set()? { return Err(Status::SecurityStatusNotSatisfied); } - // DESIGN Set PIN: always confirm with touch button + // DESIGN (see design.md): Set PIN: always confirm with touch button self.user_present()?; let command::SetPin { password } = set_pin; @@ -1198,7 +1198,7 @@ where if !self._extension_is_pin_set()? { return Err(Status::SecurityStatusNotSatisfied); } - // DESIGN Change PIN: always confirm with touch button + // DESIGN (see design.md): Change PIN: always confirm with touch button self.user_present()?; let command::ChangePin {