{"record":{"id":"80ebf39548abb325","repo":"AlexxIT/go2rtc","slug":"hap-validatesignature","errorCode":null,"errorMessage":"hap: ValidateSignature","messagePattern":"hap: ValidateSignature","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/hap/client.go","lineNumber":171,"sourceCode":"\tif err != nil {\n\t\treturn\n\t}\n\n\t// 3. unpack payload from TLV8\n\tvar plainM2 struct {\n\t\tIdentifier string `tlv8:\"1\"`\n\t\tSignature  string `tlv8:\"10\"`\n\t}\n\tif err = tlv8.Unmarshal(b, &plainM2); err != nil {\n\t\treturn\n\t}\n\n\t// 4. verify signature for M2 response with device public\n\t// device session + device id + our session\n\tif c.DevicePublic != nil {\n\t\tb = Append(cipherM2.PublicKey, plainM2.Identifier, sessionPublic)\n\t\tif !ed25519.ValidateSignature(c.DevicePublic, b, []byte(plainM2.Signature)) {\n\t\t\treturn errors.New(\"hap: ValidateSignature\")\n\t\t}\n\t}\n\n\t// STEP M3: send our clientID to device\n\t// 1. generate signature with our private key\n\t// (our session + our ID + device session)\n\tb = Append(sessionPublic, c.ClientID, cipherM2.PublicKey)\n\tif b, err = ed25519.Signature(c.ClientPrivate, b); err != nil {\n\t\treturn\n\t}\n\n\t// 2. generate payload\n\tplainM3 := struct {\n\t\tIdentifier string `tlv8:\"1\"`\n\t\tSignature  string `tlv8:\"10\"`\n\t}{\n\t\tIdentifier: c.ClientID,\n\t\tSignature:  string(b),","sourceCodeStart":153,"sourceCodeEnd":189,"githubUrl":"https://github.com/AlexxIT/go2rtc/blob/c245815e75e2a5fd60b4290f12bfc04e55a984d3/pkg/hap/client.go#L153-L189","documentation":"During Dial's STEP M2, the client verifies the accessory's response signature using the device's long-term ed25519 public key (DevicePublic) over device-LTPK || device-identifier || client-session-public. This error means the signature did not validate: the peer is not the device this client was paired with, or the stored device public key is stale.","triggerScenarios":"The accessory was factory-reset or re-paired with another controller, so its LTPK no longer matches the stored DevicePublic; connecting to the wrong device that answers on the same mDNS name/IP; a man-in-the-middle impersonating the accessory; corrupted stored pairing data.","commonSituations":"Camera or hub was reset and re-paired with a different phone; IP address reuse after DHCP reassignment points the client at a different accessory; restoring a client's config backup onto a network with new hardware.","solutions":["Re-pair with the accessory (run the Pair flow again) to refresh DevicePublic and the session keys.","Verify you are connecting to the correct device: check that mDNS entry Info[\"id\"] matches your DeviceID.","Delete the stale pairing on the accessory (factory reset or remove controller) and pair again.","If this recurs unexpectedly, treat it as a security event: confirm no MITM and that the device identity is genuine."],"exampleFix":"// before: stale DevicePublic after device re-pair -> Dial fails\nif err := client.Dial(); err != nil {\n    if strings.Contains(err.Error(), \"ValidateSignature\") {\n        // device LTPK changed: re-pair to refresh keys\n        if err := pairDevice(client); err != nil { return err }\n        err = client.Dial()\n    }\n}\n// after: proactively refresh pairing when the identity mismatch is detected\n// (pairDevice runs the M1-M6 setup and updates client.DevicePublic)","handlingStrategy":"validation","validationCode":"if client.DevicePublic == nil || len(client.DevicePublic) != ed25519.PublicKeySize {\n    return errors.New(\"no valid stored device public key; pair the device first\")\n}","typeGuard":"func hasStoredDeviceKey(c *hap.Client) bool {\n    return len(c.DevicePublic) == ed25519.PublicKeySize\n}","tryCatchPattern":"if err := client.Dial(); err != nil {\n    if strings.Contains(err.Error(), \"ValidateSignature\") {\n        // device LTPK mismatch: re-pair to refresh DevicePublic\n        if e := pairDevice(client); e != nil { return e }\n        return client.Dial()\n    }\n    return err\n}","preventionTips":["Refresh stored DevicePublic whenever the accessory is factory-reset or re-paired elsewhere.","Match the mDNS entry id to DeviceID before connecting to avoid talking to the wrong device.","Treat validation failures as security events — never proceed with unverified device keys."],"tags":["hap","ed25519","signature","pairing","security"],"backgroundTag":"signature-verification-failed","analyzedSha":"c245815e75e2a5fd60b4290f12bfc04e55a984d3","analyzedAt":"2026-09-07T11:47:02.965Z","contentChangedAt":"2026-09-07T11:47:02.965Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}