Advisories for Cargo/Nimiq-Primitives package

2026

nimiq-primitives: Panic in TrieProof::verify via child_index unwrap on equal-length keys

A malicious peer acting as a state-sync source can crash a syncing node by sending a crafted TrieChunk whose proof contains two TrieProofNodes with identical keys. TrieProof::verify() calls TrieProofNode::child_index() (primitives/src/trie/trie_proof_node.rs:94), which unconditionally unwraps KeyNibbles::get(self.key.len()). Because is_prefix_of returns true for two equal keys, execution reaches get(len), which returns None, and the unwrap() panics. The panic is reached from untrusted network input (ResponseChunk → commit_chunks → put_chunk → proof.verify()) before any cryptographic …

nimiq-primitives: Out-of-bounds panic in KeyNibbles::Add from oversized child suffix in a deserialized proof

A malicious peer acting as a state-sync source can crash a syncing node with a crafted TrieChunk whose proof contains a TrieNodeChild whose suffix, when concatenated with the parent key via KeyNibbles::Add, exceeds the fixed 63-byte backing array. Add (primitives/src/key_nibbles.rs:332 / :341) indexes bytes[self.bytes_len()..self.bytes_len() + other.bytes_len()] with no combined-length check, causing an out-of-bounds slice panic (both the even- and odd-length branches). KeyNibbles deserialization validates only the individual length <= 126, …

nimiq-primitives: Panic DoS in trie chunk processing via ROOT-keyed item

A remote, unauthenticated denial-of-service vulnerability in MerkleRadixTrie::put_chunk allows any state-sync peer to crash any node performing state synchronization (freshly joining nodes and recovering nodes). A malicious peer can respond to a RequestChunk with a ResponseChunk::Chunk whose first TrieItem.key is the empty (ROOT) key. The chunk passes sorting, range, and Merkle-proof validation, but when put_raw tries to store a value at the root node, it calls TrieNode::put_value(…).unwrap(), which returns Err(RootCantHaveValue) and …

nimiq-primitives: BlockInclusionProof interlink issue when hops are empty

A logic flaw in BlockInclusionProof::is_block_proven causes the function to return true without performing any cryptographic verification when get_interlink_hops yields an empty hop list. This occurs when the target block is at the election block position immediately preceding the election head's epoch. An attacker providing transaction inclusion proofs can forge a MacroBlock header for that epoch position and have it accepted as "proven" without any hash or signature verification.