Skip to content

feat(remote-config): verify RC container signatures#7046

Merged
rickvdl merged 13 commits into
mainfrom
rickvdl/remote-config-rc-container-signing
Jun 25, 2026
Merged

feat(remote-config): verify RC container signatures#7046
rickvdl merged 13 commits into
mainfrom
rickvdl/remote-config-rc-container-signing

Conversation

@rickvdl

@rickvdl rickvdl commented Jun 19, 2026

Copy link
Copy Markdown
Member

Part of a stack of PRs, builds on #7022.

Matches the trusted-entitlements RC Container signing behavior from RevenueCat/purchases-android#3601.

Changes

  • Implements response signature verification for the remote config endpoint
  • As described in the backend spec this only validates the first element in the response (the config blob)
  • Extracted the ElementParser out of the main RCContainer parser in order to reuse it during the signature validation for extracting the checksum
    • This is done in order to keep the RCContainer.Parser clean of any business knowledge about the config endpoint (handling the 0st element as the config blob in order to pull out it's checksum)
  • In order to keep the HTTPClient generic and not assume RCContainer == config endpoint, i've introduced the ResponseSignatureContextProvider, which can give instruct the HTTP client on how to create and validate the signatures.
    • For the current JSON endpoint this means using the request body for the request signature, using the full body for the response signature and not overriding any result based on http status code
    • For the remote config endpoint this means no request signature, using only the checksum from the config blob (the first element in RCContainer format) and overriding the 204 status code to return .verified regardless
    • This should support the remote config case while keeping the HTTP client clear of any domain logic.

This 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" .-> M
Loading

Note

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 ResponseSignatureContextProvider on request paths supplies the signed response bytes and optional request body. Default endpoints still use the full response body and POST body; remote config uses RemoteConfigSignatureContextProvider, which signs the first element’s stored 24-byte config checksum (via a shared RCContainer.ElementParser), omits the request body from signing, and treats 204 No Content as a nil response message while still verifying headers.

Signing+ResponseVerification now 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). RemoteConfigAPI completions return RemoteConfigFetchResult (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.

@emerge-tools

emerge-tools Bot commented Jun 19, 2026

Copy link
Copy Markdown

4 builds increased size

Name Version Download Change Install Change Approval
RevenueCat
com.revenuecat.PaywallsTester
1.0 (1) 18.9 MB ⬆️ 7.8 kB (0.04%) 68.4 MB ⬆️ 34.0 kB (0.05%) N/A
BinarySizeTest
com.revenuecat.binary-size-test.local-source
1.0 (1) 4.4 MB ⬆️ 2.8 kB (0.07%) 13.1 MB ⬆️ 6.6 kB (0.05%) N/A
BinarySizeTest
com.revenuecat.binary-size-test.cocoapods
1.0 (1) 6.6 MB ⬆️ 4.0 kB (0.06%) 28.9 MB ⬆️ 15.2 kB (0.05%) N/A
BinarySizeTest
com.revenuecat.binary-size-test.spm
1.0 (1) 4.5 MB ⬆️ 4.4 kB (0.1%) 11.5 MB ⬆️ 7.2 kB (0.06%) N/A

RevenueCat 1.0 (1)
com.revenuecat.PaywallsTester

⚖️ Compare build
⏱️ Analyze build performance

Total install size change: ⬆️ 34.0 kB (0.05%)
Total download size change: ⬆️ 7.8 kB (0.04%)

Largest size changes

Item Install Size Change
DYLD.String Table ⬆️ 10.1 kB
DYLD.Exports ⬆️ 1.4 kB
Code Signature ⬆️ 832 B
Other ⬆️ 21.6 kB
View Treemap

Image of diff

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
View Treemap

Image of diff

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
View Treemap

Image of diff

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
View Treemap

Image of diff


🛸 Powered by Emerge Tools

@rickvdl rickvdl force-pushed the rickvdl/remote-config-rc-container-signing branch 2 times, most recently from 42a1a7c to 945f2aa Compare June 19, 2026 10:04
@rickvdl rickvdl force-pushed the rickvdl/remote-config-api-endpoint-v2 branch from 80bb3e6 to 9434b2e Compare June 19, 2026 13:02
@rickvdl rickvdl force-pushed the rickvdl/remote-config-rc-container-signing branch from 42caf01 to bd6986d Compare June 19, 2026 13:33
@rickvdl

rickvdl commented Jun 19, 2026

Copy link
Copy Markdown
Member Author

@RCGitBot please test

@rickvdl rickvdl marked this pull request as ready for review June 19, 2026 13:57
@rickvdl rickvdl requested a review from a team as a code owner June 19, 2026 13:57
Comment thread Sources/Security/Signing+ResponseVerification.swift
@rickvdl rickvdl force-pushed the rickvdl/remote-config-api-endpoint-v2 branch 2 times, most recently from b980d5f to 969f3ef Compare June 22, 2026 09:34
@rickvdl rickvdl force-pushed the rickvdl/remote-config-rc-container-signing branch from f017e98 to 69f7b0f Compare June 22, 2026 14:47
@rickvdl rickvdl force-pushed the rickvdl/remote-config-api-endpoint-v2 branch from e199587 to f829337 Compare June 23, 2026 14:21
@rickvdl rickvdl force-pushed the rickvdl/remote-config-rc-container-signing branch from 69f7b0f to 41ad3fe Compare June 23, 2026 14:22
@rickvdl rickvdl force-pushed the rickvdl/remote-config-api-endpoint-v2 branch from f829337 to cd935bb Compare June 24, 2026 07:46
Base automatically changed from rickvdl/remote-config-api-endpoint-v2 to main June 24, 2026 08:37
@rickvdl rickvdl force-pushed the rickvdl/remote-config-rc-container-signing branch from 41ad3fe to fbcb45e Compare June 24, 2026 11:07
@rickvdl

rickvdl commented Jun 24, 2026

Copy link
Copy Markdown
Member Author

@RCGitBot please test

1 similar comment
@rickvdl

rickvdl commented Jun 25, 2026

Copy link
Copy Markdown
Member Author

@RCGitBot please test

Comment thread Sources/Security/Signing+ResponseVerification.swift

@ajpallares ajpallares left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some initial comments! But looking great so far. I'll keep reviewing now...

Comment thread Sources/Networking/HTTPClient/HTTPRequestPath.swift
Comment thread Sources/Networking/RemoteConfigAPI.swift Outdated
Comment thread Sources/Networking/HTTPClient/HTTPRequestPath.swift Outdated
Comment thread Sources/Networking/RemoteConfigSignatureContextProvider.swift
Comment thread Sources/Networking/RemoteConfigSignatureContextProvider.swift Outdated
}

func requestBodyForSignature(for request: HTTPRequest) -> HTTPRequestBody? {
return nil

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch! I'll verify this

@rickvdl rickvdl Jun 25, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As discussed we've updated the implementation to verify the request signature as described in the spec, change here

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ 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.

Comment thread Sources/Security/Signing+ResponseVerification.swift Outdated
@rickvdl

rickvdl commented Jun 25, 2026

Copy link
Copy Markdown
Member Author

@RCGitBot please test

@rickvdl rickvdl requested a review from ajpallares June 25, 2026 13:33
@rickvdl

rickvdl commented Jun 25, 2026

Copy link
Copy Markdown
Member Author

@RCGitBot please test

@rickvdl rickvdl requested a review from tonidero June 25, 2026 13:58
@rickvdl rickvdl force-pushed the rickvdl/remote-config-rc-container-signing branch from d4be3c0 to 7f539c0 Compare June 25, 2026 14:12
@rickvdl

rickvdl commented Jun 25, 2026

Copy link
Copy Markdown
Member Author

@RCGitBot please test

@rickvdl rickvdl force-pushed the rickvdl/remote-config-rc-container-signing branch from 7f539c0 to 05c7ac5 Compare June 25, 2026 14:30
@rickvdl

rickvdl commented Jun 25, 2026

Copy link
Copy Markdown
Member Author

@RCGitBot please test

@tonidero tonidero left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is looking great to me! Amazing job with that abstraction!

@ajpallares ajpallares left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for iterating on this! Looks great!

Comment thread Sources/Networking/HTTPClient/HTTPRequestPath.swift Outdated
@rickvdl

rickvdl commented Jun 25, 2026

Copy link
Copy Markdown
Member Author

@RCGitBot please test

@rickvdl rickvdl merged commit cf77437 into main Jun 25, 2026
44 checks passed
@rickvdl rickvdl deleted the rickvdl/remote-config-rc-container-signing branch June 25, 2026 15:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants