Rewrite HomeKit client

This commit is contained in:
Alexey Khit
2023-08-30 21:52:06 +03:00
parent 7d65c60711
commit 22787b979d
30 changed files with 1094 additions and 916 deletions
+53 -48
View File
@@ -37,9 +37,9 @@ type Client struct {
ClientPrivate []byte
OnEvent func(res *http.Response)
Output func(msg any)
//Output func(msg any)
conn net.Conn
Conn net.Conn
reader *bufio.Reader
}
@@ -89,21 +89,21 @@ func (c *Client) Dial() (err error) {
return false
})
if c.conn, err = net.DialTimeout("tcp", c.DeviceAddress, ConnDialTimeout); err != nil {
if c.Conn, err = net.DialTimeout("tcp", c.DeviceAddress, ConnDialTimeout); err != nil {
return
}
c.reader = bufio.NewReader(c.conn)
c.reader = bufio.NewReader(c.Conn)
// STEP M1: send our session public to device
sessionPublic, sessionPrivate := curve25519.GenerateKeyPair()
// 1. Send sessionPublic
plainM1 := struct {
PublicKey []byte `tlv8:"3"`
PublicKey string `tlv8:"3"`
State byte `tlv8:"6"`
}{
PublicKey: sessionPublic,
PublicKey: string(sessionPublic),
State: StateM1,
}
res, err := c.Post(PathPairVerify, MimeTLV8, tlv8.MarshalReader(plainM1))
@@ -113,19 +113,19 @@ func (c *Client) Dial() (err error) {
// STEP M2: unpack deviceID from response
var cipherM2 struct {
PublicKey []byte `tlv8:"3"`
EncryptedData []byte `tlv8:"5"`
PublicKey string `tlv8:"3"`
EncryptedData string `tlv8:"5"`
State byte `tlv8:"6"`
}
if err = tlv8.UnmarshalReader(res.Body, &cipherM2); err != nil {
return err
}
if cipherM2.State != StateM2 {
return NewResponseError(plainM1, cipherM2)
return newResponseError(plainM1, cipherM2)
}
// 1. generate session shared key
sessionShared, err := curve25519.SharedSecret(sessionPrivate, cipherM2.PublicKey)
sessionShared, err := curve25519.SharedSecret(sessionPrivate, []byte(cipherM2.PublicKey))
if err != nil {
return
}
@@ -138,7 +138,7 @@ func (c *Client) Dial() (err error) {
}
// 2. decrypt M2 response with session key
b, err := chacha20poly1305.Decrypt(sessionKey, "PV-Msg02", cipherM2.EncryptedData)
b, err := chacha20poly1305.Decrypt(sessionKey, "PV-Msg02", []byte(cipherM2.EncryptedData))
if err != nil {
return
}
@@ -146,7 +146,7 @@ func (c *Client) Dial() (err error) {
// 3. unpack payload from TLV8
var plainM2 struct {
Identifier string `tlv8:"1"`
Signature []byte `tlv8:"10"`
Signature string `tlv8:"10"`
}
if err = tlv8.Unmarshal(b, &plainM2); err != nil {
return
@@ -156,7 +156,7 @@ func (c *Client) Dial() (err error) {
// device session + device id + our session
if c.DevicePublic != nil {
b = Append(cipherM2.PublicKey, plainM2.Identifier, sessionPublic)
if !ed25519.ValidateSignature(c.DevicePublic, b, plainM2.Signature) {
if !ed25519.ValidateSignature(c.DevicePublic, b, []byte(plainM2.Signature)) {
return errors.New("hap: ValidateSignature")
}
}
@@ -172,10 +172,10 @@ func (c *Client) Dial() (err error) {
// 2. generate payload
plainM3 := struct {
Identifier string `tlv8:"1"`
Signature []byte `tlv8:"10"`
Signature string `tlv8:"10"`
}{
Identifier: c.ClientID,
Signature: b,
Signature: string(b),
}
if b, err = tlv8.Marshal(plainM3); err != nil {
return
@@ -188,11 +188,11 @@ func (c *Client) Dial() (err error) {
// 4. generate request
cipherM3 := struct {
EncryptedData []byte `tlv8:"5"`
EncryptedData string `tlv8:"5"`
State byte `tlv8:"6"`
}{
State: StateM3,
EncryptedData: b,
EncryptedData: string(b),
}
if res, err = c.Post(PathPairVerify, MimeTLV8, tlv8.MarshalReader(cipherM3)); err != nil {
return
@@ -206,25 +206,25 @@ func (c *Client) Dial() (err error) {
return
}
if plainM4.State != StateM4 {
return NewResponseError(cipherM3, plainM4)
return newResponseError(cipherM3, plainM4)
}
// like tls.Client wrapper over net.Conn
if c.conn, err = secure.Client(c.conn, sessionShared, true); err != nil {
if c.Conn, err = secure.Client(c.Conn, sessionShared, true); err != nil {
return
}
// new reader for new conn
c.reader = bufio.NewReaderSize(c.conn, 32*1024) // 32K like default request body
c.reader = bufio.NewReaderSize(c.Conn, 32*1024) // 32K like default request body
return
}
func (c *Client) Close() error {
if c.conn == nil {
if c.Conn == nil {
return nil
}
conn := c.conn
c.conn = nil
conn := c.Conn
c.Conn = nil
return conn.Close()
}
@@ -234,23 +234,26 @@ func (c *Client) GetAccessories() ([]*Accessory, error) {
return nil, err
}
var ac Accessories
if err = json.NewDecoder(res.Body).Decode(&ac); err != nil {
var v JSONAccessories
if err = json.NewDecoder(res.Body).Decode(&v); err != nil {
return nil, err
}
for _, accs := range ac.Accessories {
for _, serv := range accs.Services {
for _, char := range serv.Characters {
char.AID = accs.AID
}
}
}
return ac.Accessories, nil
return v.Value, nil
}
func (c *Client) GetCharacters(query string) ([]*Character, error) {
func (c *Client) GetFirstAccessory() (*Accessory, error) {
accs, err := c.GetAccessories()
if err != nil {
return nil, err
}
if len(accs) == 0 {
return nil, errors.New("hap: GetAccessories zero answer")
}
return accs[0], nil
}
func (c *Client) GetCharacters(query string) ([]JSONCharacter, error) {
res, err := c.Get(PathCharacteristics + "?id=" + query)
if err != nil {
return nil, err
@@ -261,15 +264,15 @@ func (c *Client) GetCharacters(query string) ([]*Character, error) {
return nil, err
}
var ch Characters
if err = json.Unmarshal(data, &ch); err != nil {
var v JSONCharacters
if err = json.Unmarshal(data, &v); err != nil {
return nil, err
}
return ch.Characters, nil
return v.Value, nil
}
func (c *Client) GetCharacter(char *Character) error {
query := fmt.Sprintf("%d.%d", char.AID, char.IID)
query := fmt.Sprintf("%d.%d", DeviceAID, char.IID)
chars, err := c.GetCharacters(query)
if err != nil {
return err
@@ -279,20 +282,21 @@ func (c *Client) GetCharacter(char *Character) error {
}
func (c *Client) PutCharacters(characters ...*Character) error {
var v JSONCharacters
for i, char := range characters {
if char.Event != nil {
char = &Character{AID: char.AID, IID: char.IID, Event: char.Event}
} else {
char = &Character{AID: char.AID, IID: char.IID, Value: char.Value}
}
v.Value = append(v.Value, JSONCharacter{
AID: 1,
IID: char.IID,
Value: char.Value,
})
characters[i] = char
}
data, err := json.Marshal(Characters{characters})
body, err := json.Marshal(v)
if err != nil {
return err
}
_, err = c.Put(PathCharacteristics, MimeJSON, bytes.NewReader(data))
_, err = c.Put(PathCharacteristics, MimeJSON, bytes.NewReader(body))
if err != nil {
return err
}
@@ -312,8 +316,9 @@ func (c *Client) GetImage(width, height int) ([]byte, error) {
return io.ReadAll(res.Body)
}
func (c *Client) LocalAddr() string {
return c.conn.LocalAddr().String()
func (c *Client) LocalIP() string {
addr := c.Conn.LocalAddr().(*net.TCPAddr)
return addr.IP.To4().String()
}
func DecodeKey(s string) []byte {