events.go 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  1. package qr
  2. import (
  3. "crypto/rand"
  4. "crypto/rsa"
  5. "crypto/sha256"
  6. "crypto/x509"
  7. "encoding/base64"
  8. "encoding/json"
  9. "errors"
  10. "strings"
  11. "time"
  12. "github.com/ayn2op/discordo/internal/http"
  13. "github.com/ayn2op/tview"
  14. "github.com/diamondburned/arikawa/v3/utils/httputil"
  15. "github.com/gdamore/tcell/v3"
  16. "github.com/gorilla/websocket"
  17. "github.com/skip2/go-qrcode"
  18. )
  19. type TokenEvent struct {
  20. tcell.EventTime
  21. Token string
  22. }
  23. const remoteAuthGatewayURL = "wss://remote-auth-gateway.discord.gg/?v=2"
  24. type connCreateEvent struct {
  25. tcell.EventTime
  26. conn *websocket.Conn
  27. }
  28. type connCloseEvent struct{ tcell.EventTime }
  29. func (m *Model) connect() tview.Command {
  30. return func() tcell.Event {
  31. headers := http.Headers()
  32. headers.Set("User-Agent", http.BrowserUserAgent)
  33. conn, _, err := websocket.DefaultDialer.Dial(remoteAuthGatewayURL, headers)
  34. if err != nil {
  35. return tcell.NewEventError(err)
  36. }
  37. return &connCreateEvent{conn: conn}
  38. }
  39. }
  40. func (m *Model) close() tview.Command {
  41. return func() tcell.Event {
  42. if m.conn != nil {
  43. if err := m.conn.Close(); err != nil {
  44. return tcell.NewEventError(err)
  45. }
  46. }
  47. return &connCloseEvent{}
  48. }
  49. }
  50. type helloEvent struct {
  51. tcell.EventTime
  52. heartbeatInterval int
  53. timeoutMS int
  54. }
  55. type nonceProofEvent struct {
  56. tcell.EventTime
  57. encryptedNonce string
  58. }
  59. type pendingRemoteInitEvent struct {
  60. tcell.EventTime
  61. fingerprint string
  62. }
  63. type pendingTicketEvent struct {
  64. tcell.EventTime
  65. encryptedUserPayload string
  66. }
  67. type pendingLoginEvent struct {
  68. tcell.EventTime
  69. ticket string
  70. }
  71. type cancelEvent struct{ tcell.EventTime }
  72. func (m *Model) listen() tview.Command {
  73. return func() tcell.Event {
  74. if m.conn == nil {
  75. return nil
  76. }
  77. _, data, err := m.conn.ReadMessage()
  78. if err != nil {
  79. return tcell.NewEventError(err)
  80. }
  81. var payload struct {
  82. Op string `json:"op"`
  83. }
  84. if err := json.Unmarshal(data, &payload); err != nil {
  85. return tcell.NewEventError(err)
  86. }
  87. switch payload.Op {
  88. case "hello":
  89. var payload struct {
  90. HeartbeatInterval int `json:"heartbeat_interval"`
  91. TimeoutMS int `json:"timeout_ms"`
  92. }
  93. if err := json.Unmarshal(data, &payload); err != nil {
  94. return tcell.NewEventError(err)
  95. }
  96. return &helloEvent{heartbeatInterval: payload.HeartbeatInterval, timeoutMS: payload.TimeoutMS}
  97. case "nonce_proof":
  98. var payload struct {
  99. EncryptedNonce string `json:"encrypted_nonce"`
  100. }
  101. if err := json.Unmarshal(data, &payload); err != nil {
  102. return tcell.NewEventError(err)
  103. }
  104. return &nonceProofEvent{encryptedNonce: payload.EncryptedNonce}
  105. case "pending_remote_init":
  106. var payload struct {
  107. Fingerprint string `json:"fingerprint"`
  108. }
  109. if err := json.Unmarshal(data, &payload); err != nil {
  110. return tcell.NewEventError(err)
  111. }
  112. return &pendingRemoteInitEvent{fingerprint: payload.Fingerprint}
  113. case "pending_ticket":
  114. var payload struct {
  115. EncryptedUserPayload string `json:"encrypted_user_payload"`
  116. }
  117. if err := json.Unmarshal(data, &payload); err != nil {
  118. return tcell.NewEventError(err)
  119. }
  120. return &pendingTicketEvent{encryptedUserPayload: payload.EncryptedUserPayload}
  121. case "cancel":
  122. return &cancelEvent{}
  123. case "pending_login":
  124. var payload struct {
  125. Ticket string `json:"ticket"`
  126. }
  127. if err := json.Unmarshal(data, &payload); err != nil {
  128. return tcell.NewEventError(err)
  129. }
  130. return &pendingLoginEvent{ticket: payload.Ticket}
  131. default:
  132. return nil
  133. }
  134. }
  135. }
  136. type heartbeatTickEvent struct{ tcell.EventTime }
  137. func (m *Model) heartbeat() tview.Command {
  138. return func() tcell.Event {
  139. time.Sleep(m.heartbeatInterval)
  140. return &heartbeatTickEvent{}
  141. }
  142. }
  143. func (m *Model) sendHeartbeat() tview.Command {
  144. return func() tcell.Event {
  145. if m.conn == nil {
  146. return nil
  147. }
  148. data := struct {
  149. Op string `json:"op"`
  150. }{"heartbeat"}
  151. if err := m.conn.WriteJSON(data); err != nil {
  152. return tcell.NewEventError(err)
  153. }
  154. return nil
  155. }
  156. }
  157. type privateKeyEvent struct {
  158. tcell.EventTime
  159. privateKey *rsa.PrivateKey
  160. }
  161. func (m *Model) generatePrivateKey() tview.Command {
  162. return func() tcell.Event {
  163. privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
  164. if err != nil {
  165. return tcell.NewEventError(err)
  166. }
  167. return &privateKeyEvent{privateKey: privateKey}
  168. }
  169. }
  170. func (m *Model) sendInit() tview.Command {
  171. return func() tcell.Event {
  172. if m.privateKey == nil {
  173. return tcell.NewEventError(errors.New("missing private key"))
  174. }
  175. spki, err := x509.MarshalPKIXPublicKey(m.privateKey.Public())
  176. if err != nil {
  177. return tcell.NewEventError(err)
  178. }
  179. encodedPublicKey := base64.StdEncoding.EncodeToString(spki)
  180. data := struct {
  181. Op string `json:"op"`
  182. EncodedPublicKey string `json:"encoded_public_key"`
  183. }{"init", encodedPublicKey}
  184. if err := m.conn.WriteJSON(data); err != nil {
  185. return tcell.NewEventError(err)
  186. }
  187. return nil
  188. }
  189. }
  190. func (m *Model) sendNonceProof(encryptedNonce string) tview.Command {
  191. return func() tcell.Event {
  192. decodedNonce, err := base64.StdEncoding.DecodeString(encryptedNonce)
  193. if err != nil {
  194. return tcell.NewEventError(err)
  195. }
  196. decryptedNonce, err := rsa.DecryptOAEP(sha256.New(), nil, m.privateKey, decodedNonce, nil)
  197. if err != nil {
  198. return tcell.NewEventError(err)
  199. }
  200. encodedNonce := base64.RawURLEncoding.EncodeToString(decryptedNonce)
  201. data := struct {
  202. Op string `json:"op"`
  203. Nonce string `json:"nonce"`
  204. }{"nonce_proof", encodedNonce}
  205. if err := m.conn.WriteJSON(data); err != nil {
  206. return tcell.NewEventError(err)
  207. }
  208. return nil
  209. }
  210. }
  211. type qrCodeEvent struct {
  212. tcell.EventTime
  213. qrCode *qrcode.QRCode
  214. }
  215. func (m *Model) generateQRCode(fingerprint string) tview.Command {
  216. return func() tcell.Event {
  217. content := "https://discord.com/ra/" + fingerprint
  218. qrCode, err := qrcode.New(content, qrcode.Low)
  219. if err != nil {
  220. return tcell.NewEventError(err)
  221. }
  222. qrCode.DisableBorder = true
  223. return &qrCodeEvent{qrCode: qrCode}
  224. }
  225. }
  226. type userEvent struct {
  227. tcell.EventTime
  228. discriminator string
  229. username string
  230. }
  231. func (m *Model) decryptUserPayload(encryptedPayload string) tview.Command {
  232. return func() tcell.Event {
  233. decodedPayload, err := base64.StdEncoding.DecodeString(encryptedPayload)
  234. if err != nil {
  235. return tcell.NewEventError(err)
  236. }
  237. decryptedPayload, err := rsa.DecryptOAEP(sha256.New(), nil, m.privateKey, decodedPayload, nil)
  238. if err != nil {
  239. return tcell.NewEventError(err)
  240. }
  241. parts := strings.Split(string(decryptedPayload), ":")
  242. if len(parts) != 4 {
  243. return tcell.NewEventError(errors.New("invalid user payload"))
  244. }
  245. return &userEvent{discriminator: parts[1], username: parts[3]}
  246. }
  247. }
  248. func (m *Model) exchangeTicket(ticket string) tview.Command {
  249. return func() tcell.Event {
  250. headers := http.Headers()
  251. headers.Set("Referer", "https://discord.com/login")
  252. if m.fingerprint != "" {
  253. headers.Set("X-Fingerprint", m.fingerprint)
  254. }
  255. client := http.NewClient("")
  256. client.OnRequest = append(client.OnRequest, httputil.WithHeaders(headers))
  257. encryptedToken, err := client.ExchangeRemoteAuthTicket(ticket)
  258. if err != nil {
  259. return tcell.NewEventError(err)
  260. }
  261. decodedToken, err := base64.StdEncoding.DecodeString(encryptedToken)
  262. if err != nil {
  263. return tcell.NewEventError(err)
  264. }
  265. decryptedToken, err := rsa.DecryptOAEP(sha256.New(), nil, m.privateKey, decodedToken, nil)
  266. if err != nil {
  267. return tcell.NewEventError(err)
  268. }
  269. return &TokenEvent{Token: string(decryptedToken)}
  270. }
  271. }