Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Transport Encryption for Mitosis #18

Open
wants to merge 34 commits into
base: master
Choose a base branch
from

Conversation

rossica
Copy link

@rossica rossica commented Jan 2, 2020

Abstract

These changes implement transport encryption between the keyboard halves and the receiver.
Hard-coded encryption keys are used to protect randomly-generated keys which are used to encrypt the bulk of transfers. Periodically, new randomly-generated keys are created and used.
Hardware features of the NRF51 SoC are utilized where possible to reduce impact to battery life as much as possible.

Encryption Implementation

Authenticated encryption with associated data (AEAD) is used to secure data sent between the keyboard halves and the receiver, using AES in Counter mode (AES-CTR) for confidentiality, and Cipher-based Message Authentication Code (AES-CMAC) for authenticity. It is implemented as Encrypt-then-MAC.
AES was selected due to the presence of a hardware AES engine in the NRF51 SoC. CMAC was similarly selected over HMAC to take advantage of the hardware AES engine, and due to reduced memory footprint.

Key Generation

A single master seed value is used as input to Cipher-based Key Derivation Function (CKDF), along with per-device salt values, which generates the AES-128 key for AES-CTR encryption/decryption, the nonce value for AES-CTR, and the key for CMAC.
The keys generated from the master seed are used to establish a root-of-trust for data sent to the keyboard halves from the receiver, and are used as the initial transport keys for data sent from the keyboard halves to the receiver, until session keys are ready.
CKDF was selected over HKDF to take advantage of the hardware AES engine.

Over-the-Air Data Format

Data messages from the keyboard halves to the receiver use the following format:

|                     1                   2                   3
| 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 |
|-----------------------------------------------------------------|
|               encrypted payload                |     key ID     | 
|-----------------------------------------------------------------|
|                           counter value                         |
|-----------------------------------------------------------------|
|                    message authentication code                  |
|-----------------------------------------------------------------|
|               message authentication code (contd.)              |
|-----------------------------------------------------------------|
|               message authentication code (contd.)              |
|-----------------------------------------------------------------|
|               message authentication code (contd.)              |
|_________________________________________________________________|

The encrypted payload is the key matrix bitmap that has been encrypted using the keyboard's encryption key, and the counter value.
The key ID is used to distinguish which decryption key to use for a given keyboard half. The key ID is incremented for each rekeying the occurs. Key IDs are not unique between keyboard halves, and may be in use by both keyboard halves at the same time. Key IDs may also be reused by the same keyboard half, provided it is not the same as the current key ID.
The message authentication code is calculated over the encrypted payload, key ID, and counter value.

Rekeying messages from the receiver to a keyboard half use the following format:

|                     1                   2                   3
| 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 |
|-----------------------------------------------------------------|
|                         encrypted key seed                      |
|-----------------------------------------------------------------|
|                    encrypted key seed (contd.)                  |
|-----------------------------------------------------------------|
|                    encrypted key seed (contd.)                  |
|-----------------------------------------------------------------|
|          encrypted key seed (contd.)           |   new key ID   | 
|-----------------------------------------------------------------|
|                    message authentication code                  |
|-----------------------------------------------------------------|
|               message authentication code (contd.)              |
|-----------------------------------------------------------------|
|               message authentication code (contd.)              |
|-----------------------------------------------------------------|
|               message authentication code (contd.)              |
|_________________________________________________________________|

The encrypted seed is 15 randomly-generated bytes that have been encrypted using the receiver's root-of-trust key.
The message authentication code is calculated over the encrypted seed and key ID.

Rekeying Process

As soon as the receiver powers on, the hardware random number generator (RNG) is enabled and begins generating seed values to be used to derive the transport session key, nonce, and CMAC key for each keyboard half. When the receiver receives a message from a keyboard half that is using the initial root-of-trust keys, the receiver responds with a randomly generated seed, encrypted using the receiver's root-of-trust key, and monotonically-incremented key ID. The encryption of the seed uses the new key ID as the counter value.
The receiver must guarantee the key ID is not the same as the current key ID and is not zero (0). Key ID 0 is reserved for the initial key, and can never be used to identify any other encryption key.
The receiver will also respond with a new encrypted seed after the keyboard half's counter value exceeds a chosen threshold.
Upon receiving the encrypted seed, the keyboard half verifies the MAC, then decrypts the seed and generates a new nonce, encryption key, and CMAC key using CKDF with the same salt values as in the initial key generation. All subsequent messages sent by the keyboard half will use the new key and key ID to distinguish them from the previous encryption key.

Testing

All cryptographic algorithms were tested and verified with officially-published test vectors, where available, and executed on an AMD64 system. It is assumed the hardware AES engine in NRF51 is correct, and it was not tested itself. The tests can be found in the mitosis-crypto/test directory.
End to end testing was manually performed with Mitosis keyboard hardware using the precompiled-crypto-* firmware in the precompiled directory. Testing covered: typical start up; resetting the receiver and not the keyboard halves; resetting the keyboard halves and not the receiver; and rekeying of a keyboard half once its counter exceeded a threshold. In all cases, the keyboard continued operation with no apparent loss of key presses or delay.

Other Changes

During development, it was discovered that the main loop on the receiver was very sensitive to timing disruptions and would de-sync with the UART, causing the SoC to reset. This was particularly frequent when generating the random seed values, and computing the new encryption keys for rekeying. Decryption of messages didn't seem to affect it, however.
In order to solve this problem, the UART code was changed from a polling-based approach to a callback-based approach. The UART callback executes at the highest priority, so it supercedes any cryptographic work going on in the main loop, and can interrupt the Gazell callbacks.
This removes most of the timing sensitivity from the UART code, because it always takes priority. Responsiveness is unaffected, and it simplifies the UART code.

Miscellaneous Future Improvements

Various thoughts and features I would like to have, but aren't necessary and I don't have time for:

  • Investigate allowing the receiver to go to deep sleep between UART callbacks, when no rekeying work needs to be done. Open questions:
    • Do interrupts on NRF51 wake the CPU?
    • Does Gazell have interrupts that will wake the CPU to process them?
  • Investigate whether hardware AES-CCM support in NRF51 SoC is simpler to use/faster/more efficient than the current implementation of AES-CTR + CMAC.
  • Implement a more complex transport protocol which doesn't use the root-of-trust keys for data protection/transport. For example:
    • The keyboard halves request session key material from the receiver before transmitting data.
    • The receiver uses a randomly generated key to protect the rekeying seed, sent along with the rekeying seed.
    • The keyboard halves use the hardware RNG to generate their own session keys and transmit them before or with each key press.
  • Reduce the amount of data per-message the MAC takes up.
  • Enable mitosis_ckdf_extract to handle salt values longer than 16 bytes, either by hashing or using CMAC.

Addresses #12

rossica and others added 30 commits June 23, 2019 23:08
The assumption is that AES-CMAC will be faster than HMAC-SHA256 because of the
AES hardware acceleration, however testing doesn't seem to show any difference.
AES-CMAC seems to *feel* faster, i.e. less perceived delay between button press
and glyph appearing on-screen.

During testing, it was noticed that transmit failures increased sharply as
distance increased; this may be due to electromagnetic interference in the
testing environment, or perhaps added processing delay on the receiver from
AES-CMAC vs. HMAC-SHA256.  Additional testing or perfomance diagnostics will
be needed to rule out potential delays as a factor.
When reusing a mitosis_cmac_context, the plaintext_index and multiblock flag
weren't being reset in mitosis_cmac_complete(), which caused calls subsequent
to the first to generate incorrect hashes.
This commit fixes this bug by resetting that state in the complete() call.

Also added a test for this particular scenario to catch it in the future.
@jcharnetsky
Copy link

Are there plans to merge this pull request? My main concern about using the mitosis firmware is its lack of encryption.

@rossica
Copy link
Author

rossica commented Jun 19, 2020

@jcharnetsky I'd be happy to have it merged, but I haven't seen any interest here. It looks like this project is dead.

@LucidityCrash
Copy link

@rossica do you mind if we adapt your code for the Redox ?

@rossica
Copy link
Author

rossica commented Aug 23, 2020

@LucidityCrash Feel free to, with the caveat that I don't know about the Redox project, and I make no guarantees about the fitness of this code for that purpose, nor any guarantees about the security of the adapted code. I recommend you have a cryptography expert review your adapted code.
Although I didn't include any copyright notices, I consider my contribution here to be the same license as mitosis uses.

@rudism
Copy link

rudism commented Oct 29, 2021

@rossica thank you for all your work on this! Took care of the one nagging concern I had about my Mitosis.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants