| ***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. 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, and note that the callback |
| might be executed before `GetInstance` returns, if the Encryptor is already |
| available. 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. Note that Encryptors |
| obtained with this flag might not always operate correctly in all |
| processes as they might fallback to OSCrypt sync internally, but are |
| always safe to use from browser process. |
| 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. |