Restrict Access to TOTP Authenticator Key

Hello,

I already sent this to Bitwarden support, pasting it here as well.

I can think of several implementations to handle the OTP code without exposing the OTP seed, first we need a feature…

Bitwaden Core Feature: OTP code derivation service hook

Add a feature to specify OTP code derivation service as external Restful service with two methods:

  1. Seal OTP seed
  2. Derive OTP code out of sealed OTP seed

Authentication will performed using short term public key signed JWT issued by Bitwarden based on the vault access permission.

The service serve as a driver to provide a custom logic to perform the seal and derivation.

We may need to modify OTP seed field in vault to be opaque long string instead of structured one as sealed seed may be long.

The existing implementation will be applied, if no Restful OTP derivation service URL is registered.

openapi: 3.0.0
info:
  title: OTP derivation service
  description: |-
    This is an example of OTP derivation service that may be used
    by password managers to derive OTP code while not exposing the
    OTP seed to the client.
  contact:
    name: Alon Bar-Lev
    email: [email protected]
  version: 0.0.0
paths:
  /sealOTPseed:
    post:
      summary: Seal OTP seed
      description: Seal a OTP seed by the service keys
      operationId: sealOTPseed
      requestBody:
        description: Seal a OTP seed by the service keys
        content:
          application/json:
            schema:
              type: object
              required:
                - seed
              properties:
                name:
                  type: string
                  description: a name for the seed
                type:
                  type: string
                  default: TOTP
                seed:
                  type: string
                  example: SFDF3 SFDF3 SFDF3 44FS G432
                algorithm:
                  type: string
                  default: SHA1
                tokenLength:
                  type: integer
                  default: 6
        required: true
      responses:
        '400':
          $ref: '#/components/responses/UnsupportedOTP'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '200':
          description: Successful operation
          content:
            application/json:
              schema:
                type: object
                properties:
                  sealedSecret:
                    type: string
                    example: cGFzc3dvcmQcGFzc3dvcmQcGFzc3dvcmQcGFzc3dvcmQ
  /deriveOTPtoken:
    post:
      summary: Derive OTP token
      description: Derive OTP token out of sealed seed
      operationId: deriveOTPtoken
      requestBody:
        description: Derive OTP token out of sealed seed
        content:
          application/json:
            schema:
              type: object
              properties:
                sealedSeed:
                  type: string
                  example: cGFzc3dvcmQcGFzc3dvcmQcGFzc3dvcmQcGFzc3dvcmQ
        required: true
      responses:
        '400':
          $ref: '#/components/responses/InvalidOPTSeal'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '200':
          description: Successful operation
          content:
            application/json:
              schema:
                type: object
                required:
                  - token
                properties:
                  token:
                    type: string
                    example: 123456
                  expiration:
                    type: string
                    format: date-time
                  sealedSeed:
                    type: string
                    description: |
                      A new seed to be provided in next iteration
                      This is an advanced feature which requires writable
                      vault at user side.
                    example: 3112332c3dvcmQcGFzc3dvcmQcGFzc3dvcmQcGFzc3EErrdvcmQ
components:
  responses:
    UnauthorizedError:
      description: Access token is missing or invalid
    UnsupportedOTP:
      description: OTP settings are unsupported or invalid
    InvalidOPTSeal:
      description: The provided OTP seal is unsupported
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
security:
  - bearerAuth: []

Bitwaden UI Feature: OTP user interface modifications

  1. User enters OTP seed:
    1. If no OTP code derivation service URL is registered to the collection/object: store the OTP seed as-is
    2. Else: Send the OTP seed to the service seal method and apply the output as OTP seed in vault
  2. User resolves OTP code:
    1. If no OTP code derivation service URL is registered to the collection/object: Derive the OTP code from the OTP seed locally.
    2. Else: Send the OTP seed to the service and apply output as OTP code

Advanced:

When user enters TOTP seed, user may choose to enter the sealed seed instead of the plaintext, when entering the sealed seed there is no need to call the service.

Solution principals

  1. Do not modify current product behavior, if no OTP code derivation Restful URL is provided the current logic applies.
  2. Access control to the OTP seed will be managed by the Bitwarden, no change in this regard.
  3. Implementation may choose not to make the OTP seed available at client side.
  4. OTP seed is not available to Bitwarden (aka zero-knowledge), unless Bitwarden implements an OTP code derivation Restful service.
  5. There may be multiple OTP code derivation services deployed each one may protect different set of keys if it will be possible to specify a URL per collection/entry.

Restful OTP code derivation service implementation

AWS based example of customer’s Restful service implementation:

  1. AWS lambda that uses KMS CMK to encrypt the OTP seed and metadata and encode using base64.
  2. AWS lambda that implements the Restful interface to validate the Bitwarden ticket and decrypt the TOTP seed and metadata and derive the code.

Enclave based example of Restful service implementation: Instead of lambda + KMS use enclave + platform generated key, this can run within HSM or within other secure environments such as Intel SGX.

Bitwarden may provide a default implementation of the OTP code derivation service, the default implementation will use Bitwarden CMK encryption, it may be as secure as the device Bitwarden uses. As Bitwarden have access to the OTP seed but not to the password, it still provide a good separation of concerns, better than having both the password and the TOTP seed exposed to the client.

In any case this implementation will allow customers to replace the service with their own implementation.

There may be more complex solution in which code derivation can be done using keys that are derived from Bitwarden and the customer, however, I think the above outlined solution is simple and provides a decent solution.

Regards,
Alon Bar-Lev