OpenNHP/opennhp · error

extractInitiatorStaticPubKey: unknown cipher scheme

Error message

extractInitiatorStaticPubKey: unknown cipher scheme %d (pubkey length %d)

What it means

The packet header's CipherScheme field holds a value that is neither CIPHER_SCHEME_CURVE (0) nor CIPHER_SCHEME_GMSM (1). extractInitiatorStaticPubKey (and the wider parser) only knows these two schemes, so the decrypted static field cannot be length-validated or trusted. This almost always means a corrupted header or a sender speaking a newer/foreign protocol dialect.

Solutions

  1. Drop the packet and log the source address; if it repeats from one IP, firewall it as probing.
  2. Confirm sender and receiver run the same opennhp version with the same CIPHER_SCHEME_* constants.
  3. Verify relay integrity — a relay that rewrites packets can corrupt header bytes; upgrade or replace it.
  4. If you genuinely need a new scheme, add it to NewCipherSuite and to every scheme switch (extractInitiatorStaticPubKey, validatePeer) before sending traffic with it.

Example fix

// before (custom sender)
hdr.SetCipherScheme(2) // unknown to this server
// after
hdr.SetCipherScheme(common.CIPHER_SCHEME_CURVE)
Defensive patterns

Strategy: validation

Validate before calling

switch hdr.CipherScheme() {
case common.CIPHER_SCHEME_CURVE, common.CIPHER_SCHEME_GMSM:
	// ok
default:
	return fmt.Errorf("rejecting packet with unknown cipher scheme %d from %s", hdr.CipherScheme(), remoteAddr)
}

Type guard

func isKnownScheme(s int) bool {
	return s == common.CIPHER_SCHEME_CURVE || s == common.CIPHER_SCHEME_GMSM
}

Try / catch

peerPk, err := extractInitiatorStaticPubKey(dev, ciphers, header)
if err != nil {
	log.Error("drop packet scheme=%d from=%s: %v", header.CipherScheme(), remoteAddr, err)
	return err // drop; consider short-term ban on repeats from same IP
}

Prevention

When it happens

Trigger: UDP packet corruption flipping the scheme bits in the header; a sender built from a fork that introduced a third cipher scheme value; random internet traffic/scans hitting the hidden UDP port whose bytes happen to parse as a header with an out-of-range scheme.

Common situations: Scanners probing the NHP UDP port; mixed fork/official deployments; memory corruption or relay bugs rewriting header bytes; future scheme added server-side before agents know it (or vice versa).

Understand the failure class

Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.

Related errors


AI-assisted analysis of OpenNHP/opennhp@6e04ca5ff0 (2026-09-07). Data as JSON: /api/errors/06e5708487bae4cf. Report an issue: GitHub.

Appendix: source

Thrown at nhp/core/responder.go:199

	// Validate the length explicitly before returning so future
	// breakage manifests as an error here, not as cookie failures
	// further down. Pass nil for the dst so Open allocates exactly
	// the right size.
	peerPk, err := aead.Open(nil, header.NonceBytes(), header.StaticBytes(), chainHash.Sum(nil))
	if err != nil {
		return nil, fmt.Errorf("extractInitiatorStaticPubKey: open: %w", err)
	}
	switch header.CipherScheme() {
	case common.CIPHER_SCHEME_CURVE:
		if len(peerPk) != PublicKeySize {
			return nil, fmt.Errorf("extractInitiatorStaticPubKey: curve scheme expected %d-byte pubkey, got %d", PublicKeySize, len(peerPk))
		}
	case common.CIPHER_SCHEME_GMSM:
		if len(peerPk) != PublicKeySizeEx {
			return nil, fmt.Errorf("extractInitiatorStaticPubKey: gmsm scheme expected %d-byte pubkey, got %d", PublicKeySizeEx, len(peerPk))
		}
	default:
		return nil, fmt.Errorf("extractInitiatorStaticPubKey: unknown cipher scheme %d (pubkey length %d)", header.CipherScheme(), len(peerPk))
	}
	return peerPk, nil
}

type ResponderScheme interface {
	CreatePacketParserData(d *Device, pd *PacketData) (ppd *PacketParserData, err error)
	DerivePacketParserDataFromPrevAssemblerData(mad *MsgAssemblerData, pkt *Packet, initTime int64) (ppd *PacketParserData)
	validatePeer(d *Device, ppd *PacketParserData) (err error)
	decryptBody(d *Device, ppd *PacketParserData) (err error)
}

type CookieStore struct {
	CurrCookie     [CookieSize]byte
	PrevCookie     [CookieSize]byte
	LastCookieTime int64
}

func (cs *CookieStore) Set(cookie []byte) {

View on GitHub (pinned to 6e04ca5ff0)