feat(remote-config): verify RC container signatures#7046
Conversation
4 builds increased size
RevenueCat 1.0 (1)
|
| Item | Install Size Change |
|---|---|
| DYLD.String Table | ⬆️ 10.1 kB |
| DYLD.Exports | ⬆️ 1.4 kB |
| Code Signature | ⬆️ 832 B |
| Other | ⬆️ 21.6 kB |
BinarySizeTest 1.0 (1)
com.revenuecat.binary-size-test.local-source
⚖️ Compare build
📦 Install build
⏱️ Analyze build performance
Total install size change: ⬆️ 6.6 kB (0.05%)
Total download size change: ⬆️ 2.8 kB (0.07%)
Largest size changes
| Item | Install Size Change |
|---|---|
| 📝 RevenueCat.RCContainer.ElementParser.checksumString(in) | ⬆️ 1.5 kB |
| 🗑 RevenueCat.RCContainer.Parser.checksumString(in) | ⬇️ -1.5 kB |
| 📝 RevenueCat.RCContainer.ElementParser.base64URLString(in) | ⬆️ 1.2 kB |
| 🗑 RevenueCat.RCContainer.Parser.base64URLString(in) | ⬇️ -1.2 kB |
| 📝 RevenueCat.RemoteConfigFetchResult.value witness | ⬆️ 984 B |
BinarySizeTest 1.0 (1)
com.revenuecat.binary-size-test.cocoapods
⚖️ Compare build
📦 Install build
⏱️ Analyze build performance
Total install size change: ⬆️ 15.2 kB (0.05%)
Total download size change: ⬆️ 4.0 kB (0.06%)
Largest size changes
| Item | Install Size Change |
|---|---|
| DYLD.String Table | ⬆️ 5.1 kB |
| 📝 RevenueCat.RCContainer.ElementParser.checksumString(in) | ⬆️ 1.5 kB |
| 🗑 RevenueCat.RCContainer.Parser.checksumString(in) | ⬇️ -1.5 kB |
| 📝 RevenueCat.RCContainer.ElementParser.base64URLString(in) | ⬆️ 1.2 kB |
| 🗑 RevenueCat.RCContainer.Parser.base64URLString(in) | ⬇️ -1.2 kB |
BinarySizeTest 1.0 (1)
com.revenuecat.binary-size-test.spm
⚖️ Compare build
📦 Install build
⏱️ Analyze build performance
Total install size change: ⬆️ 7.2 kB (0.06%)
Total download size change: ⬆️ 4.4 kB (0.1%)
Largest size changes
| Item | Install Size Change |
|---|---|
| 📝 RevenueCat.RCContainer.ElementParser.checksumString(in) | ⬆️ 1.5 kB |
| 🗑 RevenueCat.RCContainer.Parser.checksumString(in) | ⬇️ -1.5 kB |
| 📝 RevenueCat.RCContainer.ElementParser.base64URLString(in) | ⬆️ 1.2 kB |
| 🗑 RevenueCat.RCContainer.Parser.base64URLString(in) | ⬇️ -1.2 kB |
| 📝 RevenueCat.RemoteConfigFetchResult.value witness | ⬆️ 984 B |
🛸 Powered by Emerge Tools
42a1a7c to
945f2aa
Compare
80bb3e6 to
9434b2e
Compare
42caf01 to
bd6986d
Compare
|
@RCGitBot please test |
b980d5f to
969f3ef
Compare
f017e98 to
69f7b0f
Compare
e199587 to
f829337
Compare
69f7b0f to
41ad3fe
Compare
f829337 to
cd935bb
Compare
41ad3fe to
fbcb45e
Compare
|
@RCGitBot please test |
1 similar comment
|
@RCGitBot please test |
fbcb45e to
0e2b685
Compare
ajpallares
left a comment
There was a problem hiding this comment.
Some initial comments! But looking great so far. I'll keep reviewing now...
| } | ||
|
|
||
| func requestBodyForSignature(for request: HTTPRequest) -> HTTPRequestBody? { | ||
| return nil |
There was a problem hiding this comment.
Hmm seeing the khepri spec, I actually think that the signature in this case covers the request context:
Empty; the signature still covers the request context (api key, nonce, path, request time), so the response is replay- and tamper-evident.
So I think there's some signature verification to do here?
There was a problem hiding this comment.
Good catch! I'll verify this
There was a problem hiding this comment.
As discussed we've updated the implementation to verify the request signature as described in the spec, change here
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 008b656. Configure here.
|
@RCGitBot please test |
|
@RCGitBot please test |
d4be3c0 to
7f539c0
Compare
|
@RCGitBot please test |
7f539c0 to
05c7ac5
Compare
|
@RCGitBot please test |
tonidero
left a comment
There was a problem hiding this comment.
This is looking great to me! Amazing job with that abstraction!
ajpallares
left a comment
There was a problem hiding this comment.
Thanks for iterating on this! Looks great!
|
@RCGitBot please test |





Part of a stack of PRs, builds on #7022.
Matches the trusted-entitlements RC Container signing behavior from RevenueCat/purchases-android#3601.
Changes
ElementParserout of the mainRCContainerparser in order to reuse it during the signature validation for extracting the checksumRCContainer.Parserclean of any business knowledge about the config endpoint (handling the 0st element as the config blob in order to pull out it's checksum)HTTPClientgeneric and not assumeRCContainer == config endpoint, i've introduced theResponseSignatureContextProvider, which can give instruct the HTTP client on how to create and validate the signatures.204status code to return.verifiedregardlessThis is only unit tested so far, as we don't have a live endpoint to test against yet. But since it's unused it should not be a big deal for now.
Architecture
flowchart TD A["HTTPClient"] --> B{"ResponseSignatureContextProvider"} B -->|"Default provider"| C["Current JSON endpoints"] C --> D["Use response body as signature payload"] D --> E["Verify signature"] E --> F["Existing JSON decoding"] B -->|"Remote config provider"| G["RC Container endpoint"] G --> H["Read config element for signing"] H --> I["Validate config checksum"] I --> J["Use checksum as signature payload"] J --> K["Verify signature"] K --> L["RemoteConfigAPI"] L --> M["Parse full RCContainer"] M --> N["Return container + verification result"] O["RCContainer.ElementParser"] O -. "used by signing path" .-> H O -. "used by full container parser" .-> MNote
High Risk
Changes security-critical response verification and remote config trust semantics; enforced mode can fail requests on bad or missing signatures, and the remote config callback type now includes verification outcome.
Overview
Enables backend response signature verification for the remote config endpoint and wires it through the existing HTTP signing pipeline without hard-coding RC Container logic in the generic client.
A new
ResponseSignatureContextProvideron request paths supplies the signed response bytes and optional request body. Default endpoints still use the full response body and POST body; remote config usesRemoteConfigSignatureContextProvider, which signs the first element’s stored 24-byte config checksum (via a sharedRCContainer.ElementParser), omits the request body from signing, and treats 204 No Content as a nil response message while still verifying headers.Signing+ResponseVerificationnow builds the verify payload through that provider; payload derivation failures log and return.failed. Remote config is listed among paths that support verification (still no nonce).RemoteConfigAPIcompletions returnRemoteConfigFetchResult(container +VerificationResult) instead of a bare optional container.Reviewed by Cursor Bugbot for commit e633213. Bugbot is set up for automated code reviews on this repo. Configure here.