[PM-32695] Cipher blob encryption#1122
Conversation
🔍 SDK Breaking Change DetectionSDK Version:
Breaking change detection uses the build of the SDK from this branch, including any incompatibities pre-existing on or merged into this branch. Check the workflow logs to confirm. |
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## vault/pm-32696/blob-aware-decrypt #1122 +/- ##
=====================================================================
+ Coverage 83.92% 83.94% +0.01%
=====================================================================
Files 435 435
Lines 56736 56918 +182
=====================================================================
+ Hits 47614 47778 +164
- Misses 9122 9140 +18 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
| pub(crate) fn blob_err_to_crypto(err: BlobEncryptionError) -> CryptoError { | ||
| tracing::warn!(error = %err, error_debug = ?err, "blob decryption failed"); | ||
| match err { | ||
| BlobEncryptionError::Crypto(c) => c, | ||
| BlobEncryptionError::SealedBlob(_) | BlobEncryptionError::NoBlobData => { | ||
| CryptoError::Decrypt | ||
| BlobEncryptionError::SealedBlob(_) => CryptoError::Decrypt, | ||
| } | ||
| } |
There was a problem hiding this comment.
This would work better as impl From<BlobEncryptionError> for CryptoError{...}, which also would let you just use ? anywhere you need this conversion.
| if organization_id.is_some() { | ||
| return false; | ||
| } | ||
| client | ||
| .internal | ||
| .get_key_store() | ||
| .context() | ||
| .get_security_state_version() | ||
| >= BLOB_SECURITY_VERSION |
There was a problem hiding this comment.
Would it work to put this logic inside the CompositeEncryptable impl for Cipher? Since it uses the KeyStoreContext internally and not feature flags this logic would still be compatible without needing CiphersClient internals.
|



🎟️ Tracking
PM-32695
📔 Objective
Potential alternative to #1076.
Routes blob-encrypted ciphers through the encrypt path symmetrically to the decrypt-side dispatch added in PM-32696. A new
EncryptMode<CipherView>selects per-cipher whether to seal sensitive content into the blobformat or take the legacy field-level path, with
CiphersClientchoosing based on the user's security state version. Builds on #1121.Key changes
EncryptMode<T>enum (Blob(T)/Legacy(T)) withCompositeEncryptable<…, Cipher>impl onEncryptMode<CipherView>. The blob arm callsencrypt_blob_cipher; the legacy arm delegates to the existingCompositeEncryptableimpl onCipherView.IdentifyKeydelegates to the inner view soencrypt_liststill picks the correct scope key.CiphersClient::should_use_blob_encryption(lifted to a free function so admin paths can share it) gates dispatch: personal-vault ciphers qualify once the user's security state reachesBLOB_SECURITY_VERSION;organization ciphers stay legacy until PM-32430.
EncryptModebefore callingkey_store.encrypt:CiphersClient::encrypt,encrypt_list,encrypt_cipher_for_rotation,create_cipher/edit_cipher, and the adminequivalents.
encrypt_blob_cipher_with_wrapping_keyvariant forencrypt_cipher_for_rotation, which installs the new user key under aLocalslot rather than the view's naturalUser/Organizationslot.Cipher.namebecomesOption<EncString>— blob-encrypted ciphers don't carry a top-level name (it lives inside the sealed blob). Strict-decrypt and legacy paths still treat a missing name as an error.is_blob_encryptedremoved; replaced bytry_parse_blobthat parses theSealedCipherBlobonce and passes it intodecrypt_blob_cipher, so the legacy/blob branch and the unsealshare work.