Skip to content

concept: SmartphoneApp

Timo Sturm edited this page Nov 8, 2021 · 17 revisions

For comments please use the issue https://github.com/privacyidea/privacyidea/issues/627

For the push token specific part of the smartphone app see: concept: pushtoken

Overall Requirements of the privacyIDEA Smartphone App

  • The Smartphone App should be compatible to HOTP/TOTP.
  • The App should provide an easy enrollment process, which should be secure.
  • The App should be usable also if the smartphone has no internet connection.
  • The App should be customizable for partners or customers.

Compatible to HOTP/TOTP

The initial version of the App should simply display OTP values according to HOTP or TOTP.

The App should be capable of running several profiles.

Later versions could support:

  • OCRA
  • Some kind of push/click Authentication

Changes to PushTokens in version 4.+

Because the Firebase API changed, we are no longer able to configure Firebase at runtime. Instead, the Firebase configuration is 'hard coded' by using the google-services.json (for android) and GoogleService-Info.plist (for iOS).

What are the consequences for the user?

  1. Users of app version 4.0.0 (or later) will automatically use the included firebase project.
  2. The server can only communicate with the user if it uses the same firebase project.
  3. The app will (probably) get a new firebase-token, this must be communicated to the privacyIDEA server (manually?).
  4. Polling (manually and automatically) is not affected by this change.

As a consequence, only servers that have access to the firebase server can push challenges, while polling is unaffected. Old PushTokens should work fine after syncing the app's firebase token with the server.

Enrollment process

The enrollment process can be similar to this of the Google Authenticator App. We can use an intent that either can be clicked on the smartphone or transported in a QR-Code.

But the QR-Code should contain some more information.

  1. The QR-Code MUST not contain the plain secret key to avoid copying the token. See: https://netknights.it/en/the-problem-with-the-google-authenticator/

  2. The QR-Code contains only a part of the OTP secret.

  3. The QR-Code can contain the information for customization like:

    • Look and Feel
    • Is the part of the OTP secret encrypted? Then the user would have to get the password via another channel.
    • Does the token need to be PIN protected?
    • Should this be the only token in the App?
    • Where does the nonce need to be sent via web request (http-destination)

If the user needs to, he will enter a password to decrypt the part of the secret. He will enter a password to protect the secret.

The App will create a nonce and either send it via HTTP or the user will enter the nonce in an enrollment page.

The App and the backend will create the OTP secret from

  • the part of the secret and
  • the nonce.

Offline Capable

The enrollment process and also authentication should be able to perform offline.

In this case, a nonce is displayed to the user during the enrollment process, which the user needs to enter in the enrollment dialog.

Customizable

  • The App should have a configurable look and feel.
  • It should be able to restrict the number of profiles/tokens e.g. to only one.
  • Use Password to protect App or not.
  • Allow HOTP
  • Allow TOTP
  • Enrollment process offline

This way the app could be configured to password protect a single TOTP-only token.

Specification

Avoiding Token Copies

We want to make it impossible to copy tokens, but we can differentiate between two kinds of token copies:

  • authorized token copies: The user themselves creates a copy of the token, e.g. to have a backup. However, the token administrator wants to prevent users from creating personal token copies. Assuming that the user has total control over the smartphone, authorized copies will always be possible, but copying should be as hard as possible.
  • unauthorized token copies: An adversary scans the QR code and creates their own copy of the token without the user's knowledge. We assume that the user's smartphone is not compromised.

There are several implementation approaches:

  1. The QR code contains the encrypted token secret. The user needs to enter a passphrase which must be transmitted via an additional channel. This approach does not require network communication between privacyIDEA and the app. However, it does not protect against authorized token copies, as the user can use the QR code and the passphrase to copy the token. It protects against unauthorized copies only if the channels are strictly separated (e.g. it does not help if the passphrase is displayed right next to the QR code) and if the passphrase is strong enough to make offline attacks against the encrypted secret infeasible.

  2. The QR code contains no secret, the smartphone generates the complete secret and sends it to privacyIDEA. This requires network (HTTPS) communication between the app and privacyIDEA. Also, privacyIDEA then completely gives away control over the key generation process, which may be a bad idea, as we probably cannot be sure about the quality of the smartphone's random number generator. However, this approach mitigates unauthorized token copies pretty effectively (if the HTTPS connection to privacyIDEA works properly). Additionally, authorized token copies are not so easy, yet of course still possible (they could, e.g., MITM themselves and sniff the HTTPS traffic or access the smartphone's storage to find out the secret), but significantly harder than just keeping the QR code.

    In theory, we could even do without the HTTPS communication, as the user could enter the secret into privacyIDEA by hand. However, this would be cumbersome due to the secret's size, and would again enable authorized copies.

  3. The QR code contains part of the secret, the smartphone generates the rest of the secret and sends it to privacyIDEA. Again, this requires HTTPS connectivity between app and privacyIDEA, but it has multiple advantages:

    • privacyIDEA still controls part of the key generation process, so a bad random number generator on the smartphone can be mitigated to some extent.
    • Authorized copies are hard, as the user would need to extract the smartphone-generated part of the secret.
    • Unauthorized copies are hard, assuming that the smartphone-generated part of the secret is big enough to make offline attacks infeasible.
    • We could again operate even without network connectivity. Clearly, this would again make authorized copies easier, but the rollout would be less cumbersome than with approach (2) as the user does not need to enter the complete secret, but only the smartphone-generated part.

Splitting secret generation between privacyIDEA and smartphone

(WARNING: Brainstorming ahead!)

Approach (3) from above requires a scheme to generate one part of the token secret on the server and the other part in the app. There may be multiple ways to do that:

  1. Split the token secret bytestring into two parts, e.g. if the total secret size is 20 bytes, we could split it into 12 bytes (generated by privacyIDEA) and 8 bytes (generated by the app). However, this seems risky (see, e.g., length extension attacks -- they are not really directly applicable here as we do not hash the secret, but they attack a similar construction).
  2. Use HMAC-SHA1 (or -256, ...): The message could be the privacyIDEA-generated part of the secret, the key could be the app-generated part. The HMAC is then used as the secret key. However, the output length always corresponds to the hash digest size (e.g. 160 bits for SHA-1), which may not correspond to the size we need for the secret. Also, it is unclear whether using HMAC output as a secret is secure.
  3. Use a key derivation function, e.g. PBKDF2 or HKDF. Some examples can be found here. These typically take a password/key material and a salt. We could, for example, generate the password on the server and the salt in the app, then use a KDF to derive the token secret on both sides. Both PBKDF2 and HKDF allow to specify an output length, i.e. the size of the token secret.

Example implementation

At the moment the default key generation function works like described in 1). If the client sends a hex string as a client component, the client component will just replace the last part of the OTP key.

E.g. the server generates the hex string:

 "3132333435363738393031323334353637383930"

The client/app will generate:

 "AABBCCDDEE"

So the overall key will combine to:

  "313233343536373839303132333435AABBCCDDEE"

API specification

Independent on how the symmetric key will be generated from the smartphone component and the privacyIDEA component, we can already take a look at the API.

We simply assume, that there is a key generation function:

key = generate_sym_key( phone_component, PI_component, some_options)

The PI_component would be sent in the first enrollment request from privacyIDEA to the user, who e.g. could scan a QR code.

The phone_compnent would be sent in the second enrollment request from the phone or the user to privacyIDEA.

Enrollment Step 1

Request:

POST /token/init
type=...
genkey=1
[2step=1]

Usually the tokentype type would define, that this is a two step enrollment or we could add a parameter 2step=1 to indicate this. In case we have a normal TOTP Smartphone token, we might want to avoid another tokentype... ...maybe. As everything else behaves just the same.

A tokenclass may contain an indicator, that this token can be enrolled in two steps. (similar to self.mode)

  • Internally the token field rollout_state would be set to a certain value, indicating that the token is not completely enrolled, yet.
  • The token could also be set to disabled.
  • The PI_component would be generated and be sent back in the response.

Response:

The detail section of the response would contain the PI_component. If the PI_component is contained in the "otpkey", we probably need an indicator to indicate, that the token is not enrolled, yet.

Enrollment Step 2

The smartphone/token now would generate smartphone_component. Let us assume, it displays the component for user input/interaction.

..note:: Then privacyIDEA needs to show a second dialog, where the user can enter the smartphone_component.

Then the second request would be sent to privacyIDEA:

POST /token/init
serial=...
otpkey=...

This request needs to pass the serial number of the token. privacyIDEA can look up the token. It also can check if the token is in the correct enrollment state.

If so, privacyIDEA can run the pluggable function:

generate_sym_key(PI_component, smartphone_component)

with PI_component generated during the first request and smartphone_component sent in the second request.

  • The generated key will be written as otp key to the database
  • the token will be activated
  • the enrollment state will be deleted.

=> Token can be used.

Key generation

Each token class could define its own method generate_sym_key.

Secret Generation using PBKDF2

see privacyIDEA documentation on 2step enrollment

Clone this wiki locally