AlexxIT/go2rtc · error

hap: ValidateSignature

Error message

hap: ValidateSignature

What it means

In Pair's STEP M6, the client verifies the accessory's signature over its signed data using the ed25519 public key carried inside the M6 message itself. This error means the accessory's self-attested identity did not verify — the exchange is untrustworthy, so Pair aborts before comparing DeviceID.

Solutions

  1. Retry Pair on a fresh session — transient corruption resolves on a clean exchange.
  2. Ensure only the intended accessory responds: match the mDNS entry id to DeviceID and remove duplicates on the network.
  3. Power-cycle the accessory and repeat pairing; if it still fails, update or replace the firmware.
  4. Treat repeated failures as a security signal: the peer cannot prove its identity, so do not persist pairing data from the attempt.

Example fix

// before: Pair failure leaves partial state persisted
err := client.Pair(pin)
savePairing(client) // persisted even on ValidateSignature failure
// after: persist only after a fully verified pairing
if err := client.Pair(pin); err != nil {
    if strings.Contains(err.Error(), "ValidateSignature") {
        return errors.New("device identity could not be verified; pairing aborted")
    }
    return err
}
savePairing(client)
Defensive patterns

Strategy: try-catch

Validate before calling

entries := mdns.Query(mdns.ServiceHAP)
ids := map[string]int{}
for _, e := range entries { if e.Complete() { ids[e.Info["id"]]++ } }
for id, n := range ids {
    if n > 1 { return fmt.Errorf("duplicate HAP device id %s on network", id) }
}

Try / catch

if err := client.Pair(pin); err != nil {
    if strings.Contains(err.Error(), "ValidateSignature") {
        // identity not verifiable: abort, do not persist pairing data
        return errors.New("device identity verification failed; check for duplicate devices or bad firmware, then retry on a fresh session")
    }
    return err
}

Prevention

When it happens

Trigger: A corrupted or truncated M6 response over a flaky transport; a device (or MITM) answering the pairing exchange without valid long-term key material; firmware bug producing a malformed M6 payload; mixing pairing responses from two devices (e.g. duplicate mDNS entries).

Common situations: Two accessories on the network exposing the same mDNS name so the client talks to the wrong one; experimental/clone firmware with broken signing; radio-level packet corruption during pairing; replayed M6 from a previous aborted pairing.

Related errors


AI-assisted analysis of AlexxIT/go2rtc@c245815e75 (2026-09-07). Data as JSON: /api/errors/94bdd52878bf2a6f. Report an issue: GitHub.

Appendix: source

Thrown at pkg/hap/client_pairing.go:266

		Identifier string `tlv8:"1"`
		PublicKey  string `tlv8:"3"`
		Signature  string `tlv8:"10"`
	}{}
	if err = tlv8.Unmarshal(b, &plainM6); err != nil {
		return
	}

	// STEP M6. Verify payload
	remoteSign, err := hkdf.Sha512(
		sessionShared, "Pair-Setup-Accessory-Sign-Salt", "Pair-Setup-Accessory-Sign-Info",
	)
	if err != nil {
		return
	}

	b = Append(remoteSign, plainM6.Identifier, plainM6.PublicKey)
	if !ed25519.ValidateSignature([]byte(plainM6.PublicKey), b, []byte(plainM6.Signature)) {
		return errors.New("hap: ValidateSignature")
	}

	if c.DeviceID != plainM6.Identifier {
		return errors.New("hap: wrong DeviceID: " + plainM6.Identifier)
	}

	c.DevicePublic = []byte(plainM6.PublicKey)

	return nil
}

func (c *Client) ListPairings() error {
	plainM1 := struct {
		Method byte `tlv8:"0"`
		State  byte `tlv8:"6"`
	}{
		Method: MethodListPairings,
		State:  StateM1,

View on GitHub (pinned to c245815e75)