// SetupEncryptionKey calculates and internally sets encryption key `K` based on salt and info // // Only 32 bytes are used from HKDF-SHA512 func (p *SetupServerSession) SetupEncryptionKey(salt []byte, info []byte) error { hash, err := hkdf.Sha512(p.PrivateKey, salt, info) if err == nil { p.EncryptionKey = hash } return err }
// SetupEncryptionKey generates an encryption key based on the shared key, salt and info. func (s *VerifySession) SetupEncryptionKey(salt []byte, info []byte) error { hash, err := hkdf.Sha512(s.SharedKey[:], salt, info) if err == nil { s.EncryptionKey = hash } return err }
// SetupEncryptionKey calculates encryption key `K` based on salt and info. func (s *SetupClientSession) SetupEncryptionKey(salt []byte, info []byte) error { hash, err := hkdf.Sha512(s.PrivateKey, salt, info) if err == nil { s.EncryptionKey = hash } return err }
// NewSecureClientSessionFromSharedKey returns a session from a shared secret key to simulate a HomeKit client. // This is currently only used for testing. func NewSecureClientSessionFromSharedKey(sharedKey [32]byte) (Cryptographer, error) { salt := []byte("Control-Salt") out := []byte("Control-Write-Encryption-Key") in := []byte("Control-Read-Encryption-Key") var s = new(secureSession) var err error s.encryptKey, err = hkdf.Sha512(sharedKey[:], salt, out) s.encryptCount = 0 if err != nil { return nil, err } s.decryptKey, err = hkdf.Sha512(sharedKey[:], salt, in) s.decryptCount = 0 return s, err }
// 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 }
// 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 }