Skip to content

refactor: untangle the USM decode and key code - #52

Merged
ilyam8 merged 6 commits into
msgflags-predicatesfrom
usm-decode-keys
Oct 13, 2026
Merged

ilyam8 merged 6 commits into
msgflags-predicatesfrom
usm-decode-keys

Conversation

@ilyam8

@ilyam8 ilyam8 commented Oct 13, 2026 •

Copy link
Copy Markdown
Member

Summary

Phase 6 (guardrails), USM decode and keys. No behavior change, no exported API change.

  • One engine-ID adoption. USM unmarshal and setSecurityParameters each wrote the adoption (take the ID, drop
    both keys, derive them for it); both call adoptEngineID now. Its caller holds the lock or owns a private copy, as
    every decoder does.
  • readAuthParams. It owns what unmarshal spread over 20 lines: the alias of msgAuthenticationParameters in the
    packet, its read, the rejection of authenticated flags without a protocol, and the zeroing before the digest check,
    now marked as the known bug it is. The privacy-parameters check is one condition.
  • Protocol tables. The authentication and privacy protocol descriptions are arrays indexed by protocol value
    instead of switches.
  • Dead and wasted work. init has no error result: crypto/rand.Read never returns one since Go 1.24. The key
    cache creates the password-to-key hasher on a miss only (a hit allocated one and never used it). validate states
    each passphrase requirement as one condition.
  • Names. The two functions that sign a message (SnmpPacket, UsmSecurityParameters) are named sign, apart
    from exchange.authenticate, which verifies; their docs mark their known bugs (the recovered panic, the first
    placeholder). discoveryRequired becomes discoveryPacket. testAuthentication picks the flags and the verifying
    parameters in one place.

Validation

  • New pins, each passing on the code before the refactor: signing with a short HMAC-SHA-2 key in FIPS 140-only mode
    (a recovered panic: stack on stdout, no message, no error); decode errors for flags without a protocol (NoAuth and
    NoPriv included); a keys-only AES table user rejecting a trap from another engine; validate's order when both
    passphrases are missing.
  • Engine, FIPS 140-only, codec and USM goldens unchanged; engine log transcripts identical (2642 lines).
  • 43 mutation probes on the moved and rewritten code: 40 killed (three of them by the new pins); two cannot be told
    apart by any reachable outcome (a NoAuth table entry nothing reads; a privacy check that differs only in an error
    text, as in refactor: name the msgFlags authFlag and privFlag tests #51), and one drops the USM parameters' logger, which the engine log transcripts catch.
  • A/B (8 interleaved rounds): cached key derivation 7-11% faster with 2 fewer allocations; uncached keys, encode and
    decode unchanged.
  • go test -race, FIPS 140-only, golangci-lint, cross-builds, govulncheck: pass locally.

In FIPS 140-only mode crypto/hmac panics on a key shorter than 14
octets. The signer recovers the panic, writes the stack to stdout and
returns no message and no error, so MarshalMsg yields an empty message.
A 14-octet key signs.
…eld apart

USM unmarshal and setSecurityParameters each wrote the engine-ID
adoption (take the ID, drop both keys, derive them for it); both call
adoptEngineID now, whose caller holds the lock or owns a private copy,
as every decoder does.

readAuthParams owns what unmarshal spread over 20 lines: the alias of
msgAuthenticationParameters in the packet, its read, the rejection of
authenticated flags without an authentication protocol, and the zeroing
before the digest check, now marked as the known bug it is. The fuzz
known-panic table names the new function. The privacy-parameters check
is one condition.
- The authentication and privacy protocol descriptions are arrays
  indexed by protocol value instead of switches; values outside the
  table still describe no protocol.
- init seeds the salt counter without an error result: crypto/rand.Read
  never returns one since Go 1.24, so the branches that forwarded it,
  and validateParameters' check of it, were dead.
- The key cache creates the password-to-key hasher on a miss only; a
  hit allocated one and never used it. Its test checks that a hit
  creates none.
- validate states each passphrase requirement as one condition.
- SnmpPacket.authenticate and UsmSecurityParameters.authenticate sign a
  message; exchange.authenticate verifies one. The signers are named
  sign now, and their docs mark their known bugs: the recovered panic
  (stack on stdout, no message, no error) and the digest written over
  the first placeholder anywhere in the message. The stdout text is
  unchanged.
- discoveryRequired becomes discoveryPacket, which says what it returns;
  the discovery packet test builds its packet with it instead of a copy.
- testAuthentication chooses the flags and the verifying parameters in
  one place; the verifier is still dereferenced only when the flags ask
  for authentication.
Each case passes on the code before the USM decode refactor:

- Decoding a message whose flags ask for authentication or privacy
  fails, with its error text, when the decoder has no protocol for it,
  NoAuth and NoPriv included: the decoding counterpart of the encoding
  security-level test.
- A keys-only AES user in the trap table rejects a noAuthNoPriv trap
  from another engine: adopting the engine ID drops the keys, and no
  AES key derives from the empty passphrase (known bug).
- validate reports the missing privacy passphrase first when both are
  missing.
@ilyam8
ilyam8 added this pull request to stack #53 October 13, 2026 06:17
@ilyam8 ilyam8 changed the title usm decode keys refactor: untangle the USM decode and key code Oct 13, 2026
@ilyam8
ilyam8 marked this pull request as ready for review October 13, 2026 06:17
@ilyam8
ilyam8 merged commit c009088 into master Oct 13, 2026
22 checks passed
@ilyam8
ilyam8 deleted the usm-decode-keys branch October 13, 2026 14:22
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.

2 participants