package clientapi import ( "crypto/hmac" "crypto/sha1" //nolint:gosec // The TURN REST API is defined with HMAC-SHA1; coturn checks exactly that. "encoding/base64" "net/http" "strconv" "github.com/dariuszjaniak/warp/server/internal/config" "github.com/dariuszjaniak/warp/server/internal/httputil" "github.com/dariuszjaniak/warp/server/internal/userapi" ) type turnServerResponse struct { URIs []string `json:"uris"` Username string `json:"username"` Password string `json:"password"` TTL int64 `json:"ttl"` } // staticTURNTTL is the ttl given with static credentials. They never // change, so it only sets how often a client asks again. const staticTURNTTL = 86400 // handleTurnServer implements GET /_matrix/client/v3/voip/turnServer. // // With no TURN server configured it answers an empty object, as Synapse // does: clients take that as "no relay" and still try a direct connection, // where an error would have some of them refuse to place a call at all. func (c *ClientAPI) handleTurnServer(w http.ResponseWriter, _ *http.Request, auth userapi.Authenticated) error { t := c.turn if len(t.URIs) == 0 || (t.SharedSecret == "" && t.Username == "") { httputil.WriteJSON(w, http.StatusOK, struct{}{}) return nil } if t.SharedSecret != "" { username, password, ttl := turnRESTCredentials(t, auth.UserID, c.now().Unix()) httputil.WriteJSON(w, http.StatusOK, turnServerResponse{ URIs: t.URIs, Username: username, Password: password, TTL: ttl, }) return nil } httputil.WriteJSON(w, http.StatusOK, turnServerResponse{ URIs: t.URIs, Username: t.Username, Password: t.Password, TTL: staticTURNTTL, }) return nil } // turnRESTCredentials makes TURN REST API credentials: the username is the // expiry time and the user, and the password is the username signed with // the secret the TURN server shares, so it can check them without asking // anyone. func turnRESTCredentials(t config.TURN, userID string, now int64) (username, password string, ttl int64) { ttl = int64(t.UserLifetime.Seconds()) username = strconv.FormatInt(now+ttl, 10) + ":" + userID mac := hmac.New(sha1.New, []byte(t.SharedSecret)) mac.Write([]byte(username)) return username, base64.StdEncoding.EncodeToString(mac.Sum(nil)), ttl }