Skip to content

Cap the ohttp key body size from directory - #1846

Open
benalleng wants to merge 1 commit into
payjoin:masterfrom
benalleng:unbounded-key-res
Open

Cap the ohttp key body size from directory#1846
benalleng wants to merge 1 commit into
payjoin:masterfrom
benalleng:unbounded-key-res

Conversation

@benalleng

@benalleng benalleng commented Aug 26, 2026

Copy link
Copy Markdown
Collaborator

This caps the key config fetch size from the directory to limit any malicious directory from sending oversized data on key fetch.

Coded with GLM-5.3

Pull Request Checklist

Please confirm the following before requesting review:

@coveralls

coveralls commented Aug 26, 2026

Copy link
Copy Markdown
Collaborator

Coverage Report for CI Build 33902922471

Coverage increased (+0.006%) to 86.646%

Details

  • Coverage increased (+0.006%) from the base build.
  • Patch coverage: 9 uncovered changes across 2 files (67 of 76 lines covered, 88.16%).
  • 1 coverage regression across 1 file.

Uncovered Changes

File Changed Covered %
payjoin-cli/src/app/v2/ohttp.rs 6 0 0.0%
payjoin/src/core/io.rs 70 67 95.71%

Coverage Regressions

1 previously-covered line in 1 file lost coverage.

File Lines Losing Coverage Coverage
payjoin/src/core/io.rs 1 70.97%

Coverage Stats

Coverage Status
Relevant Lines: 16549
Covered Lines: 14339
Line Coverage: 86.65%
Coverage Strength: 341.61 hits per line

💛 - Coveralls

@benalleng benalleng changed the title Cap the ohttp key body size Cap the ohttp key body size from directory Aug 26, 2026

@xstoicunicornx xstoicunicornx left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Reviewed with Opus 4.8 and Sol 5.6, compiled and unslopped the feedback as best I could. Please let me know if any of it is not quite right/applicable.

CLI follow-up found during review - handle OhttpKeysBodyTooLarge as a directory failure instead of as a relay failure (see 199412b).

Comment thread payjoin/src/core/io.rs Outdated
}
}

let mut body = Vec::with_capacity(MAX_OHTTP_KEYS_BODY_LEN);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Vec::with_capacity(MAX_OHTTP_KEYS_BODY_LEN) is grabbing 64 KiB on every fetch when a real config is exactly 74 bytes making every client allocate ~886x what it needs.

Suggested change
let mut body = Vec::with_capacity(MAX_OHTTP_KEYS_BODY_LEN);
let mut body = Vec::new();

Comment thread payjoin/src/core/io.rs Outdated
/// the suite vector is u16-length-bounded (at most 65532 bytes of suites). Any
/// larger response cannot decode and is rejected before being fully buffered
/// to prevent memory exhaustion from a hostile payjoin directory.
pub const MAX_OHTTP_KEYS_BODY_LEN: usize = 1 + 2 + 65 + 2 + u16::MAX as usize - 3;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Perhaps pub(crate) is sufficient?

Comment thread payjoin/src/core/io.rs Outdated

let body = res.bytes().await?.to_vec();
if let Some(len) = res.content_length() {
if len as usize > MAX_OHTTP_KEYS_BODY_LEN {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Suggested change
if len as usize > MAX_OHTTP_KEYS_BODY_LEN {
if len > MAX_OHTTP_KEYS_BODY_LEN as u64 {

Converting it to usize could truncate large values on 32-bit systems.

Comment thread payjoin/src/core/io.rs Outdated
}

#[tokio::test]
async fn test_parse_oversized_body_without_content_length() {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Reqwest's content_length() reads the body's size hint, not a header, and for a Vec<u8>body that hint is exact. The test exits through the content_length branch and the chunk() loop is still not covered.

See 03b1b5f.

Comment thread payjoin/src/core/io.rs Outdated

/// Upper bound on the size of an OHTTP key configuration response body.
///
/// Derived from the ECHKeyConfig wire format: `key_id(1) + kem_id(2) +

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Suggested change
/// Derived from the ECHKeyConfig wire format: `key_id(1) + kem_id(2) +
/// Derived from the Ohttp Key Config (RFC 9458) format: `key_id(1) + kem_id(2) +

This caps the key cofig fetch size from the directory to limit any
malicious directory from sending oversized data on key fetch.

Treat oversized keys as directory failures

An oversized OHTTP key response originates from the directory, not
the CONNECT relay. Mark the directory as failed and return instead of
excluding each relay while retrying the same response.

Co-authored-by: xstoicunicornx <xstoicunicornx@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants