// Tests the pairing key verification
func TestInvalidPublicKey(t *testing.T) {
	storage, err := util.NewTempFileStorage()

	if err != nil {
		t.Fatal(err)
	}

	database := db.NewDatabaseWithStorage(storage)
	bridge, err := netio.NewSecuredDevice("Macbook Bridge", "001-02-003", database)

	if err != nil {
		t.Fatal(err)
	}

	context := netio.NewContextForSecuredDevice(bridge)

	controller := NewVerifyServerController(database, context)

	client, _ := netio.NewDevice("HomeKit Client", database)
	clientController := NewVerifyClientController(client, database)

	req := clientController.InitialKeyVerifyRequest()
	reqContainer, err := util.NewTLV8ContainerFromReader(req)

	if err != nil {
		t.Fatal(err)
	}

	reqContainer.SetByte(TagPublicKey, byte(0x01))
	// 1) C -> S
	if _, err = HandleReaderForHandler(reqContainer.BytesBuffer(), controller); err != errInvalidClientKeyLength {
		t.Fatal("expected invalid client key length error")
	}
}
Beispiel #2
0
// NewIPTransport creates a transport to provide accessories over IP.
// The pairing is secured using a 8-numbers pin.
// If more than one accessory is provided, the first becomes a bridge in HomeKit.
// It's fine when the bridge has no explicit services.
//
// All accessory specific data (crypto keys, ids) is stored in a folder named after the first accessory.
// So changing the order of the accessories or renaming the first accessory makes the stored
// data inaccessible to the tranport. In this case new crypto keys are created and the accessory
// appears as a new one to clients.
func NewIPTransport(pin string, pth string, a *accessory.Accessory, as ...*accessory.Accessory) (Transport, error) {
	// Find transport name which is visible in mDNS
	name := a.Name()
	if len(name) == 0 {
		log.Fatal("Invalid empty name for first accessory")
	}

	hapPin, err := NewPin(pin)
	if err != nil {
		return nil, err
	}

	storage, err := util.NewFileStorage(path.Join(pth, name))
	if err != nil {
		return nil, err
	}

	// Find transport uuid which appears as "id" txt record in mDNS and
	// must be unique and stay the same over time
	uuid := transportUUIDInStorage(storage)
	database := db.NewDatabaseWithStorage(storage)
	device, err := netio.NewSecuredDevice(uuid, hapPin, database)

	t := &ipTransport{
		database:  database,
		name:      name,
		device:    device,
		container: container.NewContainer(),
		mutex:     &sync.Mutex{},
		context:   netio.NewContextForSecuredDevice(device),
		emitter:   event.NewEmitter(),
	}

	t.addAccessory(a)
	for _, a := range as {
		t.addAccessory(a)
	}

	t.emitter.AddListener(t)

	return t, err
}
// 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 := netio.NewSecuredDevice("Macbook Bridge", "001-02-003", database)

	if err != nil {
		t.Fatal(err)
	}

	context := netio.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, _ := netio.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)
	}
}
Beispiel #4
0
// NewIPTransport creates a transport to provide accessories over IP.
//
// The IP transports stores the crypto keys inside a database, which
// is by default inside a folder at the current working directory.
// The folder is named exactly as the accessory name.
//
// The transports can contain more than one accessory. If this is the
// case, the first accessory acts as the HomeKit bridge.
//
// *Important:* Changing the name of the accessory, or letting multiple
// transports store the data inside the same database lead to
// unexpected behavior – don't do that.
//
// The transport is secured with an 8-digit pin, which must be entered
// by an iOS client to successfully pair with the accessory. If the
// provided transport config does not specify any pin, 00102003 is used.
func NewIPTransport(config Config, a *accessory.Accessory, as ...*accessory.Accessory) (Transport, error) {
	// Find transport name which is visible in mDNS
	name := a.Name()
	if len(name) == 0 {
		log.Fatal("Invalid empty name for first accessory")
	}

	default_config := Config{
		StoragePath: name,
		Pin:         "00102003",
		Port:        "",
	}

	if dir := config.StoragePath; len(dir) > 0 {
		default_config.StoragePath = dir
	}

	if pin := config.Pin; len(pin) > 0 {
		default_config.Pin = pin
	}

	if port := config.Port; len(port) > 0 {
		default_config.Port = ":" + port
	}

	storage, err := util.NewFileStorage(default_config.StoragePath)
	if err != nil {
		return nil, err
	}

	// Find transport uuid which appears as "id" txt record in mDNS and
	// must be unique and stay the same over time
	uuid := transportUUIDInStorage(storage)
	database := db.NewDatabaseWithStorage(storage)

	hap_pin, err := NewPin(default_config.Pin)
	if err != nil {
		return nil, err
	}

	device, err := netio.NewSecuredDevice(uuid, hap_pin, database)

	t := &ipTransport{
		database:  database,
		name:      name,
		device:    device,
		config:    default_config,
		container: container.NewContainer(),
		mutex:     &sync.Mutex{},
		context:   netio.NewContextForSecuredDevice(device),
		emitter:   event.NewEmitter(),
	}

	t.addAccessory(a)
	for _, a := range as {
		t.addAccessory(a)
	}

	t.emitter.AddListener(t)

	return t, err
}