// Handle processes a container to verify if a client is paired correctly.
func (verify *VerifyServerController) Handle(in util.Container) (util.Container, error) {
	var out util.Container
	var err error

	method := PairMethodType(in.GetByte(TagPairingMethod))

	// It is valid that method is not sent
	// If method is sent then it must be 0x00
	if method != PairingMethodDefault {
		return nil, errInvalidPairMethod(method)
	}

	seq := VerifyStepType(in.GetByte(TagSequence))

	switch seq {
	case VerifyStepStartRequest:
		if verify.step != VerifyStepWaiting {
			verify.reset()
			return nil, errInvalidInternalVerifyStep(verify.step)
		}
		out, err = verify.handlePairVerifyStart(in)
	case VerifyStepFinishRequest:
		if verify.step != VerifyStepStartResponse {
			verify.reset()
			return nil, errInvalidInternalVerifyStep(verify.step)
		}

		out, err = verify.handlePairVerifyFinish(in)
	default:
		return nil, errInvalidVerifyStep(seq)
	}

	return out, err
}
// Server -> Client
// - only error ocde (optional)
func (verify *VerifyClientController) handlePairVerifyStepFinishResponse(in util.Container) (util.Container, error) {
	code := errCode(in.GetByte(TagErrCode))
	if code != ErrCodeNo {
		fmt.Printf("Unexpected error %v\n", code)
	}

	return nil, 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:", username)
		fmt.Println("->     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
}
// Server -> Client
// - B: server public key
// - signature: from server session public key, server name, client session public key
func (verify *VerifyServerController) handlePairVerifyStart(in util.Container) (util.Container, error) {
	verify.step = VerifyStepStartResponse

	clientPublicKey := in.GetBytes(TagPublicKey)
	log.Debug.Println("->     A:", hex.EncodeToString(clientPublicKey))
	if len(clientPublicKey) != 32 {
		return nil, errInvalidClientKeyLength
	}

	var otherPublicKey [32]byte
	copy(otherPublicKey[:], clientPublicKey)

	verify.session.GenerateSharedKeyWithOtherPublicKey(otherPublicKey)
	verify.session.SetupEncryptionKey([]byte("Pair-Verify-Encrypt-Salt"), []byte("Pair-Verify-Encrypt-Info"))

	device := verify.context.GetSecuredDevice()
	var material []byte
	material = append(material, verify.session.PublicKey[:]...)
	material = append(material, device.Name()...)
	material = append(material, clientPublicKey...)
	signature, err := crypto.ED25519Signature(device.PrivateKey(), material)
	if err != nil {
		log.Info.Println(err)
		return nil, err
	}

	// Encrypt
	encryptedOut := util.NewTLV8Container()
	encryptedOut.SetString(TagUsername, device.Name())
	encryptedOut.SetBytes(TagSignature, signature)

	encryptedBytes, mac, _ := chacha20poly1305.EncryptAndSeal(verify.session.EncryptionKey[:], []byte("PV-Msg02"), encryptedOut.BytesBuffer().Bytes(), nil)

	out := util.NewTLV8Container()
	out.SetByte(TagSequence, verify.step.Byte())
	out.SetBytes(TagPublicKey, verify.session.PublicKey[:])
	out.SetBytes(TagEncryptedData, append(encryptedBytes, mac[:]...))

	log.Debug.Println("       K:", hex.EncodeToString(verify.session.EncryptionKey[:]))
	log.Debug.Println("       B:", hex.EncodeToString(verify.session.PublicKey[:]))
	log.Debug.Println("       S:", hex.EncodeToString(verify.session.PrivateKey[:]))
	log.Debug.Println("  Shared:", hex.EncodeToString(verify.session.SharedKey[:]))

	log.Debug.Println("<-     B:", hex.EncodeToString(out.GetBytes(TagPublicKey)))

	return out, nil
}
// Client -> Server
// - A: client public key
// - M1: proof
//
// Server -> client
// - M2: proof
// or
// - auth error
func (setup *SetupClientController) handlePairStepVerifyResponse(in util.Container) (util.Container, error) {
	serverProof := in.GetBytes(TagProof)
	fmt.Println("->     M2:", hex.EncodeToString(serverProof))

	if setup.session.IsServerProofValid(serverProof) == false {
		return nil, fmt.Errorf("M2 %s is invalid", hex.EncodeToString(serverProof))
	}

	err := setup.session.SetupEncryptionKey([]byte("Pair-Setup-Encrypt-Salt"), []byte("Pair-Setup-Encrypt-Info"))
	if err != nil {
		return nil, err
	}

	fmt.Println("        K:", hex.EncodeToString(setup.session.EncryptionKey[:]))

	// 2) Send username, LTPK, signature as encrypted message
	hash, err := 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, setup.client.Name()...)
	material = append(material, setup.client.PublicKey()...)

	signature, err := crypto.ED25519Signature(setup.client.PrivateKey(), material)
	if err != nil {
		return nil, err
	}

	encryptedOut := util.NewTLV8Container()
	encryptedOut.SetString(TagUsername, setup.client.Name())
	encryptedOut.SetBytes(TagPublicKey, []byte(setup.client.PublicKey()))
	encryptedOut.SetBytes(TagSignature, []byte(signature))

	encryptedBytes, tag, err := chacha20poly1305.EncryptAndSeal(setup.session.EncryptionKey[:], []byte("PS-Msg05"), encryptedOut.BytesBuffer().Bytes(), nil)
	if err != nil {
		return nil, err
	}

	out := util.NewTLV8Container()
	out.SetByte(TagPairingMethod, 0)
	out.SetByte(TagSequence, PairStepKeyExchangeRequest.Byte())
	out.SetBytes(TagEncryptedData, append(encryptedBytes, tag[:]...))

	fmt.Println("<-   Encrypted:", hex.EncodeToString(out.GetBytes(TagEncryptedData)))

	return out, nil
}
Example #6
0
func (endpoint *PairVerify) ServeHTTP(response http.ResponseWriter, request *http.Request) {
	log.Printf("[VERB] %v POST /pair-verify", request.RemoteAddr)
	response.Header().Set("Content-Type", netio.HTTPContentTypePairingTLV8)

	key := endpoint.context.GetConnectionKey(request)
	session := endpoint.context.Get(key).(netio.Session)
	ctlr := session.PairVerifyHandler()
	if ctlr == nil {
		log.Println("[VERB] Create new pair verify controller")
		ctlr = pair.NewVerifyServerController(endpoint.database, endpoint.context)
		session.SetPairVerifyHandler(ctlr)
	}

	var err error
	var in util.Container
	var out util.Container
	var secSession crypto.Cryptographer

	if in, err = util.NewTLV8ContainerFromReader(request.Body); err == nil {
		out, err = ctlr.Handle(in)
	}

	if err != nil {
		log.Println(err)
		response.WriteHeader(http.StatusInternalServerError)
	} else {
		io.Copy(response, out.BytesBuffer())

		// When key verification is done, switch to a secure session
		// based on the negotiated shared session key
		b := out.GetByte(pair.TagSequence)
		switch pair.VerifyStepType(b) {
		case pair.VerifyStepFinishResponse:
			if secSession, err = crypto.NewSecureSessionFromSharedKey(ctlr.SharedKey()); err == nil {
				log.Println("[VERB] Setup secure session")
				session.SetCryptographer(secSession)
			} else {
				log.Println("[ERRO] Could not setup secure session.", err)
			}
		}
	}
}
// Handle processes a container to pair (exchange keys) with an accessory.
func (setup *SetupClientController) Handle(in util.Container) (util.Container, error) {
	method := pairMethodType(in.GetByte(TagPairingMethod))

	// It is valid that method is not sent
	// If method is sent then it must be 0x00
	if method != PairingMethodDefault {
		return nil, errInvalidPairMethod(method)
	}

	code := errCode(in.GetByte(TagErrCode))
	if code != ErrCodeNo {
		log.Println("[ERRO]", code)
		return nil, code.Error()
	}

	seq := pairStepType(in.GetByte(TagSequence))

	var out util.Container
	var err error

	switch seq {
	case PairStepStartResponse:
		out, err = setup.handlePairStepStartResponse(in)
	case PairStepVerifyResponse:
		out, err = setup.handlePairStepVerifyResponse(in)
	case PairStepKeyExchangeResponse:
		out, err = setup.handleKeyExchange(in)
	default:
		return nil, errInvalidPairStep(seq)
	}

	return out, err
}
Example #8
0
func (endpoint *Pairing) ServeHTTP(response http.ResponseWriter, request *http.Request) {
	log.Printf("[VERB] %v POST /pairings", request.RemoteAddr)
	response.Header().Set("Content-Type", netio.HTTPContentTypePairingTLV8)

	var err error
	var in util.Container
	var out util.Container

	if in, err = util.NewTLV8ContainerFromReader(request.Body); err == nil {
		out, err = endpoint.controller.Handle(in)
	}

	if err != nil {
		log.Println(err)
		response.WriteHeader(http.StatusInternalServerError)
	} else {
		io.Copy(response, out.BytesBuffer())

		// Send events based on pairing method type
		b := in.GetByte(pair.TagPairingMethod)
		switch pair.PairMethodType(b) {
		case pair.PairingMethodDelete: // pairing removed
			endpoint.emitter.Emit(event.DeviceUnpaired{})

		case pair.PairingMethodAdd: // pairing added
			endpoint.emitter.Emit(event.DevicePaired{})

		}
	}
}
Example #9
0
// 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:", username)
	log.Println("[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
}
Example #10
0
func (endpoint *PairSetup) ServeHTTP(response http.ResponseWriter, request *http.Request) {
	log.Debug.Printf("%v POST /pair-setup", request.RemoteAddr)
	response.Header().Set("Content-Type", hap.HTTPContentTypePairingTLV8)

	var err error
	var in util.Container
	var out util.Container

	key := endpoint.context.GetConnectionKey(request)
	session := endpoint.context.Get(key).(hap.Session)
	ctrl := session.PairSetupHandler()
	if ctrl == nil {
		log.Debug.Println("Create new pair setup controller")

		if ctrl, err = pair.NewSetupServerController(endpoint.device, endpoint.database); err != nil {
			log.Info.Panic(err)
		}

		session.SetPairSetupHandler(ctrl)
	}

	if in, err = util.NewTLV8ContainerFromReader(request.Body); err == nil {
		out, err = ctrl.Handle(in)
	}

	if err != nil {
		log.Info.Println(err)
		response.WriteHeader(http.StatusInternalServerError)
	} else {
		io.Copy(response, out.BytesBuffer())

		// Send event when key exchange is done
		b := out.GetByte(pair.TagSequence)
		switch pair.PairStepType(b) {
		case pair.PairStepKeyExchangeResponse:
			endpoint.emitter.Emit(event.DevicePaired{})
		}
	}
}
// Handle processes a container to pair (exchange keys) with a client.
func (setup *SetupServerController) Handle(in util.Container) (out util.Container, err error) {
	method := PairMethodType(in.GetByte(TagPairingMethod))

	// It is valid that pair method is not sent
	// If method set then it must be 0x00
	if method != PairingMethodDefault {
		return nil, errInvalidPairMethod(method)
	}

	seq := PairStepType(in.GetByte(TagSequence))

	switch seq {
	case PairStepStartRequest:
		if setup.step != PairStepWaiting {
			setup.reset()
			return nil, errInvalidInternalPairStep(setup.step)
		}

		out, err = setup.handlePairStart(in)
	case PairStepVerifyRequest:
		if setup.step != PairStepStartResponse {
			setup.reset()
			return nil, errInvalidInternalPairStep(setup.step)
		}

		out, err = setup.handlePairVerify(in)
	case PairStepKeyExchangeRequest:
		if setup.step != PairStepVerifyResponse {
			setup.reset()
			return nil, errInvalidInternalPairStep(setup.step)
		}

		out, err = setup.handleKeyExchange(in)
	default:
		return nil, errInvalidPairStep(seq)
	}

	return out, err
}
Example #12
0
// Server -> Client
// - B: server public key
// - s: salt
//
// Client -> Server
// - A: client public key
// - M1: proof
func (setup *SetupClientController) handlePairStepStartResponse(in util.Container) (util.Container, error) {
	salt := in.GetBytes(TagSalt)
	serverPublicKey := in.GetBytes(TagPublicKey)

	if len(salt) != 16 {
		return nil, fmt.Errorf("Salt is invalid (%d bytes)", len(salt))
	}

	if len(serverPublicKey) != 384 {
		return nil, fmt.Errorf("B is invalid (%d bytes)", len(serverPublicKey))
	}

	fmt.Println("->     B:", hex.EncodeToString(serverPublicKey))
	fmt.Println("->     s:", hex.EncodeToString(salt))

	// Client
	// 1) Receive salt `s` and public key `B` and generates `S` and `A`
	err := setup.session.GenerateKeys(salt, serverPublicKey)
	if err != nil {
		return nil, err
	}
	fmt.Println("        S:", hex.EncodeToString(setup.session.PrivateKey))

	// 2) Send public key `A` and proof `M1`
	publicKey := setup.session.PublicKey // SRP public key
	proof := setup.session.Proof         // M1

	fmt.Println("<-     A:", hex.EncodeToString(publicKey))
	fmt.Println("<-     M1:", hex.EncodeToString(proof))

	out := util.NewTLV8Container()
	out.SetByte(TagPairingMethod, 0)
	out.SetByte(TagSequence, PairStepVerifyRequest.Byte())
	out.SetBytes(TagPublicKey, publicKey)
	out.SetBytes(TagProof, proof)

	return out, nil
}
// Client -> Server
// - A: entity public key
// - M1: proof
//
// Server -> entity
// - M2: proof
// or
// - auth error
func (setup *SetupServerController) handlePairVerify(in util.Container) (util.Container, error) {
	setup.step = PairStepVerifyResponse
	out := util.NewTLV8Container()
	out.SetByte(TagSequence, setup.step.Byte())

	clientPublicKey := in.GetBytes(TagPublicKey)
	log.Debug.Println("->     A:", hex.EncodeToString(clientPublicKey))

	err := setup.session.SetupPrivateKeyFromClientPublicKey(clientPublicKey)
	if err != nil {
		return nil, err
	}

	clientProof := in.GetBytes(TagProof)
	log.Debug.Println("->     M1:", hex.EncodeToString(clientProof))

	proof, err := setup.session.ProofFromClientProof(clientProof)
	if err != nil || len(proof) == 0 { // proof `M1` is wrong
		log.Debug.Println("Proof M1 is wrong")
		setup.reset()
		out.SetByte(TagErrCode, ErrCodeAuthenticationFailed.Byte()) // return error 2
	} else {
		log.Debug.Println("Proof M1 is valid")
		err := setup.session.SetupEncryptionKey([]byte("Pair-Setup-Encrypt-Salt"), []byte("Pair-Setup-Encrypt-Info"))
		if err != nil {
			return nil, err
		}

		// Return proof `M2`
		out.SetBytes(TagProof, proof)
	}

	log.Debug.Println("<-     M2:", hex.EncodeToString(out.GetBytes(TagProof)))
	log.Debug.Println("        S:", hex.EncodeToString(setup.session.PrivateKey))
	log.Debug.Println("        K:", hex.EncodeToString(setup.session.EncryptionKey[:]))

	return out, nil
}
// Handle processes a container to verify if an accessory is paired correctly.
func (verify *VerifyClientController) Handle(in util.Container) (util.Container, error) {
	var out util.Container
	var err error

	method := pairMethodType(in.GetByte(TagPairingMethod))

	// It is valid that method is not sent
	// If method is sent then it must be 0x00
	if method != PairingMethodDefault {
		return nil, errInvalidPairMethod(method)
	}

	seq := VerifyStepType(in.GetByte(TagSequence))
	switch seq {
	case VerifyStepStartResponse:
		out, err = verify.handlePairStepVerifyResponse(in)
	case VerifyStepFinishResponse:
		out, err = verify.handlePairVerifyStepFinishResponse(in)
	default:
		return nil, errInvalidVerifyStep(seq)
	}

	return out, err
}
// Server -> Client
// - only sequence number
// - error code (optional)
func (verify *VerifyServerController) handlePairVerifyFinish(in util.Container) (util.Container, error) {
	verify.step = VerifyStepFinishResponse

	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[:]))

	decryptedBytes, err := chacha20poly1305.DecryptAndVerify(verify.session.EncryptionKey[:], []byte("PV-Msg03"), message, mac, nil)

	out := util.NewTLV8Container()
	out.SetByte(TagSequence, verify.step.Byte())

	if err != nil {
		verify.reset()
		log.Info.Panic(err)
		out.SetByte(TagErrCode, ErrCodeAuthenticationFailed.Byte()) // return error 2
	} else {
		in, err := util.NewTLV8ContainerFromReader(bytes.NewBuffer(decryptedBytes))
		if err != nil {
			return nil, err
		}

		username := in.GetString(TagUsername)
		signature := in.GetBytes(TagSignature)
		log.Debug.Println("    client:", username)
		log.Debug.Println(" signature:", hex.EncodeToString(signature))

		entity, err := verify.database.EntityWithName(username)
		if err != nil {
			return nil, fmt.Errorf("Client %s is unknown", username)
		}

		if len(entity.PublicKey) == 0 {
			return nil, fmt.Errorf("No LTPK available for client %s", username)
		}

		var material []byte
		material = append(material, verify.session.OtherPublicKey[:]...)
		material = append(material, []byte(username)...)
		material = append(material, verify.session.PublicKey[:]...)

		if crypto.ValidateED25519Signature(entity.PublicKey, material, signature) == false {
			log.Debug.Println("signature is invalid")
			verify.reset()
			out.SetByte(TagErrCode, ErrCodeUnknownPeer.Byte()) // return error 4
		} else {
			log.Debug.Println("signature is valid")
		}
	}

	return out, nil
}
// Server -> Client
// - B: server public key
// - encrypted message
//      - username
//      - signature: from server session public key, server name, client session public key
//
// Client -> Server
// - encrypted message
//      - username
//      - signature: from client session public key, server name, server session public key,
func (verify *VerifyClientController) handlePairStepVerifyResponse(in util.Container) (util.Container, error) {
	serverPublicKey := in.GetBytes(TagPublicKey)
	if len(serverPublicKey) != 32 {
		return nil, fmt.Errorf("Invalid server public key size %d", len(serverPublicKey))
	}

	var otherPublicKey [32]byte
	copy(otherPublicKey[:], serverPublicKey)
	verify.session.GenerateSharedKeyWithOtherPublicKey(otherPublicKey)
	verify.session.SetupEncryptionKey([]byte("Pair-Verify-Encrypt-Salt"), []byte("Pair-Verify-Encrypt-Info"))

	fmt.Println("Client")
	fmt.Println("->   B:", hex.EncodeToString(serverPublicKey))
	fmt.Println("     S:", hex.EncodeToString(verify.session.PrivateKey[:]))
	fmt.Println("Shared:", hex.EncodeToString(verify.session.SharedKey[:]))
	fmt.Println("     K:", hex.EncodeToString(verify.session.EncryptionKey[:]))

	// Decrypt
	data := in.GetBytes(TagEncryptedData)
	message := data[:(len(data) - 16)]
	var mac [16]byte
	copy(mac[:], data[len(message):]) // 16 byte (MAC)

	decryptedBytes, err := chacha20poly1305.DecryptAndVerify(verify.session.EncryptionKey[:], []byte("PV-Msg02"), message, mac, nil)
	if err != nil {
		return nil, err
	}

	decryptedIn, err := util.NewTLV8ContainerFromReader(bytes.NewBuffer(decryptedBytes))
	if err != nil {
		return nil, err
	}

	username := decryptedIn.GetString(TagUsername)
	signature := decryptedIn.GetBytes(TagSignature)

	fmt.Println("    Username:", username)
	fmt.Println("   Signature:", hex.EncodeToString(signature))

	// Validate signature
	var material []byte
	material = append(material, verify.session.OtherPublicKey[:]...)
	material = append(material, username...)
	material = append(material, verify.session.PublicKey[:]...)

	entity := verify.database.EntityWithName(username)
	if entity == nil {
		return nil, fmt.Errorf("Server %s is unknown", username)
	}

	if len(entity.PublicKey()) == 0 {
		return nil, fmt.Errorf("No LTPK available for client %s", username)
	}

	if crypto.ValidateED25519Signature(entity.PublicKey(), material, signature) == false {
		return nil, fmt.Errorf("Could not validate signature")
	}

	out := util.NewTLV8Container()
	out.SetByte(TagSequence, VerifyStepFinishRequest.Byte())

	encryptedOut := util.NewTLV8Container()
	encryptedOut.SetString(TagUsername, verify.client.Name())

	material = make([]byte, 0)
	material = append(material, verify.session.PublicKey[:]...)
	material = append(material, verify.client.Name()...)
	material = append(material, verify.session.OtherPublicKey[:]...)

	signature, err = crypto.ED25519Signature(verify.client.PrivateKey(), material)
	if err != nil {
		return nil, err
	}

	encryptedOut.SetBytes(TagSignature, signature)

	encryptedBytes, mac, _ := chacha20poly1305.EncryptAndSeal(verify.session.EncryptionKey[:], []byte("PV-Msg03"), encryptedOut.BytesBuffer().Bytes(), nil)

	out.SetBytes(TagEncryptedData, append(encryptedBytes, mac[:]...))

	return out, nil
}
// 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:", username)
		log.Debug.Println("->     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:", tlvPairKeyExchange.GetString(TagUsername))
			log.Debug.Println("<-     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
}