Skip to content

fix(core): guard keypair sign/verify against short and multibyte key inputs - #341

Open
e-desouza wants to merge 3 commits into
mainfrom
fix/issue-282-keypairs-panic
Open

fix(core): guard keypair sign/verify against short and multibyte key inputs#341
e-desouza wants to merge 3 commits into
mainfrom
fix/issue-282-keypairs-panic

Conversation

@e-desouza

Copy link
Copy Markdown
Collaborator

Why

_get_algorithm_from_key and _get_algorithm_engine_from_key performed &key[..2] unconditionally. A key shorter than two bytes panicked immediately.

Ed25519::sign and Ed25519::is_valid_message sliced &key[ED25519_PREFIX.len()..] after a byte-length guard, but byte-length ≥ 2 does not guarantee a valid UTF-8 char boundary at offset 2. A key beginning with a multibyte character (e.g. "E£") passed the guard yet panicked at the slice.

is_valid_message also had a dsig.try_into().expect(...) call that assumed the prior dsig.len() guard would always fire first — a fragile invariant that a future refactor could break silently.

Fixes #282.

What changed

  • _get_algorithm_from_key / _get_algorithm_engine_from_key: replaced &key[..2] with key.get(..2).ok_or(InvalidSecret)?.
  • _get_algorithm_engine_from_key: now composes with _get_algorithm_from_key to avoid duplicated logic.
  • Ed25519::sign / Ed25519::is_valid_message: replaced bare [ED25519_PREFIX.len()..] slice with .get(ED25519_PREFIX.len()..), which returns None on non-char-boundary.
  • is_valid_message: replaced .expect("is_valid_message") on dsig.try_into() with a match that returns false on conversion failure.

How to validate

cargo test --release

New tests: test_sign_empty_key_returns_error, test_sign_short_key_returns_error, test_is_valid_message_empty_pubkey_returns_false, test_is_valid_message_short_pubkey_returns_false, test_ed25519_sign_trait_short_key_guard.

@codecov

codecov Bot commented Jul 12, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 98.27586% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 87.03%. Comparing base (2015c85) to head (c8bbdde).

Files with missing lines Patch % Lines
src/core/keypairs/algorithms.rs 96.96% 1 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main     #341      +/-   ##
==========================================
+ Coverage   87.01%   87.03%   +0.02%     
==========================================
  Files         255      255              
  Lines       33668    33710      +42     
==========================================
+ Hits        29295    29341      +46     
+ Misses       4373     4369       -4     
Flag Coverage Δ
integration 71.90% <ø> (ø)
unit 87.63% <98.27%> (+0.02%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
src/core/keypairs/mod.rs 94.64% <100.00%> (+5.86%) ⬆️
src/core/keypairs/algorithms.rs 96.13% <96.96%> (+0.03%) ⬆️
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copilot AI 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.

Pull request overview

This PR hardens the core keypair signing and verification APIs against panics caused by short key strings and invalid UTF-8 slicing boundaries, addressing a remote DoS vector described in #282.

Changes:

  • Replaced unconditional &key[..2] prefix slicing with safe .get(..2) and error propagation in keypair dispatch.
  • Hardened Ed25519 sign / is_valid_message against invalid slicing and fragile try_into().expect/unwrap assumptions.
  • Added regression tests for empty/short key scenarios.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
src/core/keypairs/mod.rs Makes dispatching by key prefix safe (no panics on short/invalid boundaries), propagates errors in sign, returns false safely in is_valid_message, and adds regression tests.
src/core/keypairs/algorithms.rs Makes Ed25519 signing/verifying resilient to invalid slices and conversion failures, returning Err/false instead of panicking, and adds a direct-trait guard test.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/core/keypairs/mod.rs
Comment on lines +53 to 60
fn _get_algorithm_engine_from_key(key: &str) -> XRPLCoreResult<Box<dyn CryptoImplementation>> {
let prefix = key.get(..2).ok_or(XRPLKeypairsException::InvalidSecret)?;
if prefix == ED25519_PREFIX {
Ok(_get_algorithm_engine(CryptoAlgorithm::ED25519))
} else {
Ok(_get_algorithm_engine(CryptoAlgorithm::SECP256K1))
}
}
Comment thread src/core/keypairs/mod.rs
Comment on lines +371 to 375
#[test]
fn test_is_valid_message_short_pubkey_returns_false() {
assert!(!is_valid_message(&[], "", "ab"));
}
}
Address two review comments:
- `_get_algorithm_engine_from_key` now delegates through `_get_algorithm_from_key`
  and `_get_algorithm_engine` so prefix parsing exists in exactly one place and
  the two helpers can no longer drift apart.
- `test_is_valid_message_short_pubkey_returns_false` now uses a 1-byte key ("a")
  to exercise the actual <2-byte panic guard.  A new
  `test_is_valid_message_multibyte_pubkey_returns_false` uses "E£" (3 bytes, non-
  char-boundary at offset 2) to cover the multibyte-slice regression path.
@e-desouza
e-desouza force-pushed the fix/issue-282-keypairs-panic branch from c82acfb to fb4fa44 Compare July 15, 2026 16:41
The try_into() match at line 478 — which previously panicked via .unwrap()
and now correctly returns false — had no negative-path test coverage.
Add test_ed25519_is_valid_message_trait_wrong_length_key exercising:
- 1-byte decoded key (hex suffix "AB" → 1 byte ≠ 32)
- single-byte public key (too short for "ED" prefix check)
- non-char-boundary key ("E£") that would panic on a byte-indexed slice
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.

&key[..2] panics on untrusted short key / signature strings

2 participants