code.pfad.fr/gohmekit@v0.2.1/pairing/verify_client_controller.go (about) 1 package pairing 2 3 import ( 4 "crypto/ed25519" 5 "fmt" 6 "io" 7 8 "code.pfad.fr/gohmekit/pairing/crypto" 9 "code.pfad.fr/gohmekit/tlv8" 10 ) 11 12 // NewVerifyClientController implements the client logic for the pairing-verify step. 13 func NewVerifyClientController(client Device, database Database) (*VerifyClientController, error) { 14 key, err := crypto.NewKeyOnCurve25519() 15 if err != nil { 16 return nil, err 17 } 18 return &VerifyClientController{ 19 key: key, 20 client: client, 21 database: database, 22 }, nil 23 } 24 25 // VerifyClientController implements the client logic for the pairing-verify step. 26 type VerifyClientController struct { 27 key crypto.KeyOnCurve25519 28 client Device 29 database Database 30 } 31 32 // StartRequest is the initial pairing-verify request. 33 func (c VerifyClientController) StartRequest() []byte { 34 buf, err := tlv8.Marshal(struct { 35 ktlvState 36 PublicKey []byte `tlv8:"kTLVType_PublicKey"` 37 }{ 38 ktlvState: ktlvState{1}, 39 PublicKey: c.key.PublicKey(), 40 }) 41 if err != nil { 42 panic(err) 43 } 44 return buf 45 } 46 47 func paddedNonce(s string) []byte { 48 n := make([]byte, 12) 49 copy(n[4:], s) 50 return n 51 } 52 53 // FinishRequest checks the accessory initial response and generate the finish request. 54 func (c VerifyClientController) FinishRequest(r io.Reader) (response []byte, sharedSecret []byte, err error) { 55 var data struct { 56 ktlvState 57 PublicKey []byte `tlv8:"kTLVType_PublicKey"` 58 EncryptedData []byte `tlv8:"kTLVType_EncryptedData"` 59 } 60 err = tlv8.NewDecoder(r).Decode(&data) 61 if err != nil { 62 return nil, nil, err 63 } 64 65 if data.State != 2 { 66 return nil, nil, fmt.Errorf("unexpected state: %d", data.State) 67 } 68 69 accessoryPublicKey := data.PublicKey 70 if len(accessoryPublicKey) != 32 { 71 return nil, nil, fmt.Errorf("unexpected accessory key lenth: %d", len(accessoryPublicKey)) 72 } 73 sharedSecret, err = c.key.PairVerifySharedSecret(accessoryPublicKey) 74 if err != nil { 75 return nil, nil, fmt.Errorf("could not compute sharedSecret: %w", err) 76 } 77 78 aead, err := crypto.PairVerifyAEAD(sharedSecret) 79 if err != nil { 80 return nil, nil, err 81 } 82 83 { 84 encryptedData := data.EncryptedData 85 decryptedData, err := aead.Open(encryptedData[:0], paddedNonce("PV-Msg02"), encryptedData, nil) 86 if err != nil { 87 return nil, nil, fmt.Errorf("could not verify data: %w", err) 88 } 89 90 var decodedData struct { 91 Identifier []byte `tlv8:"kTLVType_Identifier"` 92 Signature []byte `tlv8:"kTLVType_Signature"` 93 } 94 err = tlv8.Unmarshal(decryptedData, &decodedData) 95 if err != nil { 96 return nil, nil, fmt.Errorf("could not decode encrypted data: %w", err) 97 } 98 accessoryPairingID := decodedData.Identifier 99 accessorySignature := decodedData.Signature 100 101 accessoryLTPK, err := c.database.GetLongTermPublicKey(accessoryPairingID) 102 if err != nil { 103 return nil, nil, err 104 } 105 106 var accessoryInfo []byte 107 accessoryInfo = append(accessoryInfo, accessoryPublicKey...) 108 accessoryInfo = append(accessoryInfo, accessoryPairingID...) 109 accessoryInfo = append(accessoryInfo, c.key.PublicKey()...) 110 if !ed25519.Verify(accessoryLTPK, accessoryInfo, accessorySignature) { 111 return nil, nil, fmt.Errorf("could not validate signature of AccessoryInfo") 112 } 113 } 114 115 { 116 // construct response 117 var iOSDeviceInfo []byte 118 iOSDeviceInfo = append(iOSDeviceInfo, c.key.PublicKey()...) 119 iOSDeviceInfo = append(iOSDeviceInfo, c.client.PairingID()...) 120 iOSDeviceInfo = append(iOSDeviceInfo, accessoryPublicKey...) 121 122 iOSDeviceSignature, err := c.client.Ed25519Sign(iOSDeviceInfo) 123 if err != nil { 124 return nil, nil, fmt.Errorf("could not sign iOSDeviceInfo: %w", err) 125 } 126 127 cleartextData, err := tlv8.Marshal(struct { 128 Identifier []byte `tlv8:"kTLVType_Identifier"` 129 Signature []byte `tlv8:"kTLVType_Signature"` 130 }{ 131 Identifier: c.client.PairingID(), 132 Signature: iOSDeviceSignature, 133 }) 134 if err != nil { 135 return nil, nil, err 136 } 137 138 encryptedData := aead.Seal(cleartextData[:0], paddedNonce("PV-Msg03"), cleartextData, nil) 139 140 response, err = tlv8.Marshal(struct { 141 ktlvState 142 EncryptedData []byte `tlv8:"kTLVType_EncryptedData"` 143 }{ 144 ktlvState: ktlvState{3}, 145 EncryptedData: encryptedData, 146 }) 147 return response, sharedSecret, err 148 } 149 } 150 151 // FinishResponse checks the response of the accessory. From now on, the connection must be encrypted 152 // using the sharedSecret computed in the FinishRequest step. 153 func (c VerifyClientController) FinishResponse(r io.Reader) error { 154 var data struct { 155 ktlvState 156 Error byte `tlv8:"kTLVType_Error"` 157 } 158 err := tlv8.NewDecoder(r).Decode(&data) 159 if err != nil { 160 return err 161 } 162 163 if data.State != 4 { 164 return fmt.Errorf("unexpected state: %d", data.State) 165 } 166 if data.Error != 0 { 167 return fmt.Errorf("unexpected error: %d", data.Error) 168 } 169 return nil 170 }