tree: 5479e2c2853cefbb31a296fdf915ed0e86c74a50 [path history] [tgz]
  1. browser/
  2. common/
  3. OWNERS
  4. README.md
components/os_crypt/async/README.md

OS Crypt Async

This directory contains the new version of OSCrypt that supports asynchronous initialization and pluggable providers. It is intended to gradually replace os_crypt::OSCrypt (AKA “OSCrypt Sync”) with a new version with improved capabilities.

Main interfaces

browser/ should only be included by code that lives in the browser process. An instance of OSCryptAsync should be constructed and held in browser and is responsible for minting Encryptor instances. //chrome holds a browser-wide instance that's accessible from g_browser_process using os_crypt_async() method.

GetInstance can be called as many times as necessary to obtain instances of Encryptor that should be used for encryption operations. Note that GetInstance returns a base::CallbackListSubscription whose destruction will cause the callback to never run. This should be stored with the same lifetime as the callback to ensure correct function. See documentation for base::CallbackList for more on this.

When calling GetInstance an Encryptor hint can be supplied. This can change the Encryption behavior of the resulting Encryptor instance, see encryptor.h for details and see below. Note that all Encryptor returned from the same instance of OSCryptAsync will always be able to decrypt each other's data.

common/ can be included by any code in any process and allows Encryptor instances to perform encrypt/decrypt operations. These EncryptString and DecryptString operations are sync and can be called on any thread, the same as with legacy os_crypt::OSCrypt.

Encryptor instances can be passed over mojo if necessary, as mojo traits exist to serialize and deserialize. If an Encryptor instance is passed to a process then that process will be able to decrypt any data encrypted with OSCryptAsync.

It is preferred to use the base::span EncryptData and DecryptData APIs, however the EncryptString and DecryptString APIs are provided for ease of compatibility with existing callers of os_crypt::OSCrypt. The string and span APIs are compatible with one another.

Integration Guide

OSCryptAsync is currently integrated into the Cookie encryption within the network service, and this code can be used as a full end-to-end example of how to integrate OSCryptAsync into your existing code that is using os_crypt::OSCrypt.

There are a few considerations that are important for integrators:

  1. GetInstance() must be called on the same sequence that it was created on, which, if you are using the instance managed by //chrome is the UI thread. Therefore, plan for your GetInstance calls to be made on this sequence. Callbacks will also arrive on this sequence. Once you have an Encryptor it can be safely passed and used on another sequence, though.
  2. Care should be taken during the rollout of any integration. In particular, the following three phase approach is recommended, although you might want to shorten this depending on your risk profile. Bear in mind that os_crypt::OSCrypt and OSCryptAsync are likely being used to seal valuable data so all precautions should be made to avoid any potential data loss.
    1. Phase 1: Convert code to async: In this first phase, the existing sync code that is calling into os_crypt::OSCrypt should be converted to perform an asynchronous initialization of an Encryptor instance by calling GetInstance and handling the callback. The Encryptor instance that is returned can then be used as if the code were calling directly to os_crypt::OSCrypt but since the Encryptor is move-only it will have to be held by an object on the calling sequence to make the calls themselves. If multiple sequences need to make encryption calls, that‘s supported, but you’ll need to get an Encryptor for each sequence and explicitly pass it to those sequences. In this phase, it is highly recommended to:
      1. Call GetInstance with the kEncryptSyncCompat option. This will ask the Encryptor to always encrypt any data in a format that is backwards compatible with os_crypt::OSCrypt. This means that if any issues are found when converting the code from sync to async, there is no risk of any permanent data loss, and any CLs can be safely rolled back, or features turned off.
      2. Land the async code behind a feature, although this might not always be possible given the restructuring required. By both using kEncryptSyncCompat and a feature flag, the code can be iterated on without risk to any permanent data loss for users.
      3. Monitor baseline metrics, to verify no guardrail metrics are impacted. If you code needs to perform Encrypt or Decrypt operations very early on in startup, then it is possible that there could be performance regressions as OSCryptAsync might not yet have obtained a valid Encryptor instance.
    2. Phase 2: Engage new Encryption: Once Phase 1 has landed and no regressions are seen, then a feature can land that removes the kEncryptSyncCompat option passed to GetInstance from all calls, and data will now start being encrypted with the keys managed by the installed OSCryptAsync key providers in a potentially non-backwards compatible way. In this phase, for example, data might start being encrypted with App-Bound encryption on supported platforms. At this point you will want to double check no data loss caused by encrypting data with the new keys, although core OSCryptAsync metrics themselves are used as guardrails against this scenario.
    3. Phase 3: Re-encrypt all data: Once Phase 2 has landed and there appear to be no regressions from using the new key, then all data currently encrypted (which will include a mix of data encrypted with os_crypt::OSCrypt, and data encrypted with OSCryptAsync depending on when it was originally encrypted) should be read in from persistent storage, decrypted, re-encrypted, and then and written back to persistent storage. This ensures that all data is now encrypted with OSCryptAsync and secured by new keys.