// Handle processes a container to pair with a new client without going through the pairing process. func (c *PairingController) Handle(cont util.Container) (util.Container, error) { method := pairMethodType(cont.GetByte(TagPairingMethod)) username := cont.GetString(TagUsername) publicKey := cont.GetBytes(TagPublicKey) log.Println("[VERB] -> Method:", method) log.Println("[VERB] -> Username:"******"[VERB] -> LTPK:", publicKey) entity := db.NewEntity(username, publicKey, nil) switch method { case PairingMethodDelete: log.Printf("[INFO] Remove LTPK for client '%s'\n", username) c.database.DeleteEntity(entity) case PairingMethodAdd: err := c.database.SaveEntity(entity) if err != nil { log.Println("[ERRO]", err) return nil, err } default: return nil, fmt.Errorf("Invalid pairing method type %v", method) } out := util.NewTLV8Container() out.SetByte(TagSequence, 0x2) return out, nil }
// Client -> Server // - encrypted tlv8: client LTPK, client name and signature (of H, client name, LTPK) // - auth tag (mac) // // Server // - Validate signature of encrpyted tlv8 // - Read and store client LTPK and name // // Server -> Client // - encrpyted tlv8: bridge LTPK, bridge name, signature (of hash `H2`, bridge name, LTPK) func (setup *SetupClientController) handleKeyExchange(in util.Container) (util.Container, error) { data := in.GetBytes(TagEncryptedData) message := data[:(len(data) - 16)] var mac [16]byte copy(mac[:], data[len(message):]) // 16 byte (MAC) fmt.Println("-> Message:", hex.EncodeToString(message)) fmt.Println("-> MAC:", hex.EncodeToString(mac[:])) decrypted, err := chacha20poly1305.DecryptAndVerify(setup.session.EncryptionKey[:], []byte("PS-Msg06"), message, mac, nil) if err != nil { fmt.Println(err) } else { decryptedBuf := bytes.NewBuffer(decrypted) in, err := util.NewTLV8ContainerFromReader(decryptedBuf) if err != nil { fmt.Println(err) } username := in.GetString(TagUsername) ltpk := in.GetBytes(TagPublicKey) signature := in.GetBytes(TagSignature) fmt.Println("-> Username:"******"-> LTPK:", hex.EncodeToString(ltpk)) fmt.Println("-> Signature:", hex.EncodeToString(signature)) entity := db.NewEntity(username, ltpk, nil) err = setup.database.SaveEntity(entity) if err != nil { fmt.Println("[ERRO]", err) } } return nil, err }
func TestDeletePairing(t *testing.T) { username := "******" entity := db.NewEntity(username, []byte{0x01, 0x02}, nil) database, _ := db.NewDatabase(os.TempDir()) database.SaveEntity(entity) in := util.NewTLV8Container() in.SetByte(TagPairingMethod, PairingMethodDelete.Byte()) in.SetByte(TagSequence, 0x01) in.SetString(TagUsername, username) controller := NewPairingController(database) out, err := controller.Handle(in) if err != nil { t.Fatal(err) } if out == nil { t.Fatal("no response") } if is, want := out.GetByte(TagSequence), byte(0x2); is != want { t.Fatalf("is=%v want=%v", is, want) } if _, err := database.EntityWithName(username); err == nil { t.Fatal("expected error") } }
// Tests the pairing key verification func TestPairVerifyIntegration(t *testing.T) { storage, err := util.NewTempFileStorage() if err != nil { t.Fatal(err) } database := db.NewDatabaseWithStorage(storage) bridge, err := hap.NewSecuredDevice("Macbook Bridge", "001-02-003", database) if err != nil { t.Fatal(err) } context := hap.NewContextForSecuredDevice(bridge) controller := NewVerifyServerController(database, context) clientDatabase, _ := db.NewTempDatabase() bridgeEntity := db.NewEntity(bridge.Name(), bridge.PublicKey(), nil) err = clientDatabase.SaveEntity(bridgeEntity) if err != nil { t.Fatal(err) } client, _ := hap.NewDevice("HomeKit Client", clientDatabase) clientEntity := db.NewEntity(client.Name(), client.PublicKey(), nil) err = database.SaveEntity(clientEntity) if err != nil { t.Fatal(err) } clientController := NewVerifyClientController(client, clientDatabase) tlvVerifyStepStartRequest := clientController.InitialKeyVerifyRequest() // 1) C -> S tlvVerifyStepStartResponse, err := HandleReaderForHandler(tlvVerifyStepStartRequest, controller) if err != nil { t.Fatal(err) } // 2) S -> C tlvFinishRequest, err := HandleReaderForHandler(tlvVerifyStepStartResponse, clientController) if err != nil { t.Fatal(err) } // 3) C -> S tlvFinishRespond, err := HandleReaderForHandler(tlvFinishRequest, controller) if err != nil { t.Fatal(err) } // 4) S -> C response, err := HandleReaderForHandler(tlvFinishRespond, clientController) if err != nil { t.Fatal(err) } if response != nil { t.Fatal(response) } }
// Client -> Server // - encrypted tlv8: entity ltpk, entity name and signature (of H, entity name, ltpk) // - auth tag (mac) // // Server // - Validate signature of encrpyted tlv8 // - Read and store entity ltpk and name // // Server -> Client // - encrpyted tlv8: bridge ltpk, bridge name, signature (of hash, bridge name, ltpk) func (setup *SetupServerController) handleKeyExchange(in util.Container) (util.Container, error) { out := util.NewTLV8Container() setup.step = PairStepKeyExchangeResponse out.SetByte(TagSequence, setup.step.Byte()) data := in.GetBytes(TagEncryptedData) message := data[:(len(data) - 16)] var mac [16]byte copy(mac[:], data[len(message):]) // 16 byte (MAC) log.Debug.Println("-> Message:", hex.EncodeToString(message)) log.Debug.Println("-> MAC:", hex.EncodeToString(mac[:])) decrypted, err := chacha20poly1305.DecryptAndVerify(setup.session.EncryptionKey[:], []byte("PS-Msg05"), message, mac, nil) if err != nil { setup.reset() log.Info.Panic(err) out.SetByte(TagErrCode, ErrCodeUnknown.Byte()) // return error 1 } else { decryptedBuf := bytes.NewBuffer(decrypted) in, err := util.NewTLV8ContainerFromReader(decryptedBuf) if err != nil { return nil, err } username := in.GetString(TagUsername) clientltpk := in.GetBytes(TagPublicKey) signature := in.GetBytes(TagSignature) log.Debug.Println("-> Username:"******"-> ltpk:", hex.EncodeToString(clientltpk)) log.Debug.Println("-> Signature:", hex.EncodeToString(signature)) // Calculate hash `H` hash, _ := hkdf.Sha512(setup.session.PrivateKey, []byte("Pair-Setup-Controller-Sign-Salt"), []byte("Pair-Setup-Controller-Sign-Info")) var material []byte material = append(material, hash[:]...) material = append(material, []byte(username)...) material = append(material, clientltpk...) if crypto.ValidateED25519Signature(clientltpk, material, signature) == false { log.Debug.Println("ed25519 signature is invalid") setup.reset() out.SetByte(TagErrCode, ErrCodeAuthenticationFailed.Byte()) // return error 2 } else { log.Debug.Println("ed25519 signature is valid") // Store entity ltpk and name entity := db.NewEntity(username, clientltpk, nil) setup.database.SaveEntity(entity) log.Debug.Printf("Stored ltpk '%s' for entity '%s'\n", hex.EncodeToString(clientltpk), username) ltpk := setup.device.PublicKey() ltsk := setup.device.PrivateKey() // Send username, ltpk, signature as encrypted message hash, err := hkdf.Sha512(setup.session.PrivateKey, []byte("Pair-Setup-Accessory-Sign-Salt"), []byte("Pair-Setup-Accessory-Sign-Info")) material = make([]byte, 0) material = append(material, hash[:]...) material = append(material, []byte(setup.session.Username)...) material = append(material, ltpk...) signature, err := crypto.ED25519Signature(ltsk, material) if err != nil { log.Info.Panic(err) return nil, err } tlvPairKeyExchange := util.NewTLV8Container() tlvPairKeyExchange.SetBytes(TagUsername, setup.session.Username) tlvPairKeyExchange.SetBytes(TagPublicKey, ltpk) tlvPairKeyExchange.SetBytes(TagSignature, []byte(signature)) log.Debug.Println("<- Username:"******"<- ltpk:", hex.EncodeToString(tlvPairKeyExchange.GetBytes(TagPublicKey))) log.Debug.Println("<- Signature:", hex.EncodeToString(tlvPairKeyExchange.GetBytes(TagSignature))) encrypted, mac, _ := chacha20poly1305.EncryptAndSeal(setup.session.EncryptionKey[:], []byte("PS-Msg06"), tlvPairKeyExchange.BytesBuffer().Bytes(), nil) out.SetByte(TagSequence, PairStepKeyExchangeRequest.Byte()) out.SetBytes(TagEncryptedData, append(encrypted, mac[:]...)) } } return out, nil }