Repository navigation
fix: draw a new salt for every encrypted SNMPv3 message - #59
Merged
Merged
Conversation
TestReencodeDrawsNewSalt encodes a packet built from a decoded encrypted message, as Netdata's trap receiver builds an inform response, twice. Both encodings carry the received salt, so the response reuses the inform's AES IV (or DES pre-IV XOR salt) with the same key. The test fails until the encoder draws a salt for every message. TestUSMLog pins that prepare hands the client's Logger to the security parameters, which Log writes to.
The salt (msgPrivacyParameters) was state the caller held: InitPacket drew it from counters inside the client's UsmSecurityParameters, and MarshalMsg encrypted with whatever the packet's PrivacyParameters held. A packet built from a decoded message, as Netdata's inform responses are, reused the received salt, so its AES IV equalled the inform's; a packet without a salt was encrypted with an empty AES salt, or panicked for DES. The encoder now draws the salt: appendV3 takes the next value of one process-wide 64-bit counter (usm.Salts, seeded from crypto/rand) for every message its flags encrypt and passes it to the USM marshaller and the cipher; the packet's PrivacyParameters are neither used nor written. DES salts come from the same counter instead of the engine boots followed by a 32-bit counter (RFC 3414 section 8.1.1.1 leaves the integer to the implementation; the receiver uses the eight octets as given). The salt counters, GoSNMP.initPacket and the USM salt helpers are gone; InitPacket does nothing and is deprecated. An SNMPv3 packet without security parameters is an error instead of a panic. Tests set the counter where they pin bytes: the encode and engine goldens, the USM fixtures, the net-snmp recordings (unchanged) and the re-encode line of the decode goldens, which uses the received salt. The engine goldens' salts change (a discovery no longer draws one, DES counts in 64 bits, the fake agent's answers draw from the same counter); a request whose OnRetry hook changes the client's privacy protocol now goes out with the packet's own; and the fake agent no longer pins the parse error of a scoped PDU decrypted with a wrong key, which depends on the salt and on the width of the platform's int.
… the seeding appendV3ScopedPDU encrypts when appendV3 drew a salt instead of reading the flags again, so one decision gives both the salt on the wire and the encryption. The tests missed two mutants. A cipher that used the packet's salt while the wire carried the drawn one went unnoticed for DES: a wrong DES IV garbles only the first eight octets, the context engine ID. The encode goldens' encrypted cases now carry a packet salt of their own (the bytes do not change), and TestReencodeDrawsNewSalt compares the decoded context fields. TestSaltsSeeded checked only that a seeded counter does not start at 1: it now requires two seeded counters to start apart and the process-wide counter to be seeded. TestSaltCounterConcurrent starts its goroutines together. The exchange.encode comment still said it draws the privacy parameters by the client's flags; it draws only the message ID now.
ilyam8
added this pull request to stack #61
October 13, 2026 22:36
stelfrag
approved these changes
Oct 14, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
First step of the bug-fix phase: the encoder draws a new salt for every encrypted SNMPv3 message.
The salt (msgPrivacyParameters, which the AES IV and the DES pre-IV XOR are built from) was state the caller held:
InitPacketdrew it from counters inside the client'sUsmSecurityParameters, andMarshalMsgencrypted withwhatever the packet's
PrivacyParametersheld. So:that way, so a response was encrypted with the inform's key and IV;
Now
appendV3takes the next value of one process-wide 64-bit counter (internal/usm, seeded from crypto/rand,atomic) for each message whose flags ask for privacy, and passes it to the USM marshaller and the cipher; the scoped
PDU is encrypted exactly when a salt was drawn. The packet's
PrivacyParametersare neither used nor written onencode; decryption still reads the received salt. A packet without security parameters is an error.
Behavior changes:
MarshalMsg,SnmpEncodePacketand requests ignore a caller'sPrivacyParameters.InitPacketdoes nothing and is deprecated (the interface keeps it).section 8.1.1.1 leaves how the salt varies to the implementation as long as IVs do not repeat, and the receiver uses
the eight octets as given. The end-to-end suite against snmpd passes for every DES and AES user.
Tests
TestReencodeDrawsNewSalt(first commit; fails on master for DES, AES and AES256C) re-encodes a decoded trap as aninform response is built: each encoding gets its own salt and decrypts.
TestUSMSaltsis rewritten for the new rule;TestUSMLogpins the logger hand-off thatinitdid andsetLoggerdoes now.nil-security-parameters(panic -> error) andwithout-salt(now equal to theirsalted twins), the
salt-9re-encodes of the USM decrypt goldens, and the engine goldens' salt values (a discoveryno longer draws one, DES counts in 64 bits, the fake agent's answers draw from the same counter). A request whose
OnRetry hook changes the client's privacy protocol now completes, because the retry encodes the request's own packet.
The net-snmp recordings are unchanged.
must not reach the wire or the cipher (their bytes are unchanged). The decode goldens re-encode with the received
salt, so "identical" still shows the codec round trip. The fake agent no longer pins the parse error of a wrong-key
decryption: those random bytes reached a 32-bit-only length error under GOARCH=386.
Validation
netsnmpplayback: pass locally;GOARCH=386 and the end-to-end suite against snmpd 5.9.3 pass in Docker.
Encode,USMEncodeandGetV3benchmarks show no time or allocation change (8interleaved rounds against master).
counters, a non-atomic counter): all killed but one. It draws the salt before the protocol and level checks, and is
accepted: which message consumes a counter value has no meaning beyond uniqueness.
fixed in the last commit.