media.go 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. package whatsapp
  2. import (
  3. "bytes"
  4. "crypto/hmac"
  5. "crypto/rand"
  6. "crypto/sha256"
  7. "encoding/base64"
  8. "encoding/json"
  9. "fmt"
  10. "github.com/Rhymen/go-whatsapp/crypto/cbc"
  11. "github.com/Rhymen/go-whatsapp/crypto/hkdf"
  12. "io"
  13. "io/ioutil"
  14. "mime/multipart"
  15. "net/http"
  16. "os"
  17. "strings"
  18. "time"
  19. )
  20. func Download(url string, mediaKey []byte, appInfo MediaType, fileLength int) ([]byte, error) {
  21. if url == "" {
  22. return nil, fmt.Errorf("no url present")
  23. }
  24. file, mac, err := downloadMedia(url)
  25. if err != nil {
  26. return nil, err
  27. }
  28. iv, cipherKey, macKey, _, err := getMediaKeys(mediaKey, appInfo)
  29. if err != nil {
  30. return nil, err
  31. }
  32. if err = validateMedia(iv, file, macKey, mac); err != nil {
  33. return nil, err
  34. }
  35. data, err := cbc.Decrypt(cipherKey, iv, file)
  36. if err != nil {
  37. return nil, err
  38. }
  39. if len(data) != fileLength {
  40. return nil, fmt.Errorf("file length does not match")
  41. }
  42. return data, nil
  43. }
  44. func validateMedia(iv []byte, file []byte, macKey []byte, mac []byte) error {
  45. h := hmac.New(sha256.New, macKey)
  46. n, err := h.Write(append(iv, file...))
  47. if err != nil {
  48. return err
  49. }
  50. if n < 10 {
  51. return fmt.Errorf("hash to short")
  52. }
  53. if !hmac.Equal(h.Sum(nil)[:10], mac) {
  54. return fmt.Errorf("invalid media hmac")
  55. }
  56. return nil
  57. }
  58. func getMediaKeys(mediaKey []byte, appInfo MediaType) (iv, cipherKey, macKey, refKey []byte, err error) {
  59. mediaKeyExpanded, err := hkdf.Expand(mediaKey, 112, string(appInfo))
  60. if err != nil {
  61. return nil, nil, nil, nil, err
  62. }
  63. return mediaKeyExpanded[:16], mediaKeyExpanded[16:48], mediaKeyExpanded[48:80], mediaKeyExpanded[80:], nil
  64. }
  65. func downloadMedia(url string) (file []byte, mac []byte, err error) {
  66. resp, err := http.Get(url)
  67. if err != nil {
  68. return nil, nil, err
  69. }
  70. if resp.StatusCode != 200 {
  71. return nil, nil, fmt.Errorf("download failed")
  72. }
  73. defer resp.Body.Close()
  74. if resp.ContentLength <= 10 {
  75. return nil, nil, fmt.Errorf("file to short")
  76. }
  77. data, err := ioutil.ReadAll(resp.Body)
  78. n := len(data)
  79. if err != nil {
  80. return nil, nil, err
  81. }
  82. return data[:n-10], data[n-10 : n], nil
  83. }
  84. func (wac *Conn) Upload(reader io.Reader, appInfo MediaType) (url string, mediaKey []byte, fileEncSha256 []byte, fileSha256 []byte, fileLength uint64, err error) {
  85. data, err := ioutil.ReadAll(reader)
  86. if err != nil {
  87. return "", nil, nil, nil, 0, err
  88. }
  89. mediaKey = make([]byte, 32)
  90. rand.Read(mediaKey)
  91. iv, cipherKey, macKey, _, err := getMediaKeys(mediaKey, appInfo)
  92. if err != nil {
  93. return "", nil, nil, nil, 0, err
  94. }
  95. enc, err := cbc.Encrypt(cipherKey, iv, data)
  96. if err != nil {
  97. return "", nil, nil, nil, 0, err
  98. }
  99. fileLength = uint64(len(data))
  100. h := hmac.New(sha256.New, macKey)
  101. h.Write(append(iv, enc...))
  102. mac := h.Sum(nil)[:10]
  103. sha := sha256.New()
  104. sha.Write(data)
  105. fileSha256 = sha.Sum(nil)
  106. sha.Reset()
  107. sha.Write(append(enc, mac...))
  108. fileEncSha256 = sha.Sum(nil)
  109. var filetype string
  110. switch appInfo {
  111. case MediaImage:
  112. filetype = "image"
  113. case MediaAudio:
  114. filetype = "audio"
  115. case MediaDocument:
  116. filetype = "document"
  117. case MediaVideo:
  118. filetype = "video"
  119. }
  120. uploadReq := []interface{}{"action", "encr_upload", filetype, base64.StdEncoding.EncodeToString(fileEncSha256)}
  121. ch, err := wac.write(uploadReq)
  122. if err != nil {
  123. return "", nil, nil, nil, 0, err
  124. }
  125. var resp map[string]interface{}
  126. select {
  127. case r := <-ch:
  128. if err = json.Unmarshal([]byte(r), &resp); err != nil {
  129. return "", nil, nil, nil, 0, fmt.Errorf("error decoding upload response: %v\n", err)
  130. }
  131. case <-time.After(wac.msgTimeout):
  132. return "", nil, nil, nil, 0, fmt.Errorf("restore session init timed out")
  133. }
  134. if int(resp["status"].(float64)) != 200 {
  135. return "", nil, nil, nil, 0, fmt.Errorf("upload responsed with %d", resp["status"])
  136. }
  137. var b bytes.Buffer
  138. w := multipart.NewWriter(&b)
  139. hashWriter, err := w.CreateFormField("hash")
  140. if err != nil {
  141. fmt.Fprintf(os.Stderr, "%v\n", err)
  142. }
  143. io.Copy(hashWriter, strings.NewReader(base64.StdEncoding.EncodeToString(fileEncSha256)))
  144. fileWriter, err := w.CreateFormFile("file", "blob")
  145. if err != nil {
  146. fmt.Fprintf(os.Stderr, "%v\n", err)
  147. }
  148. io.Copy(fileWriter, bytes.NewReader(append(enc, mac...)))
  149. err = w.Close()
  150. if err != nil {
  151. fmt.Fprintf(os.Stderr, "%v\n", err)
  152. }
  153. req, err := http.NewRequest("POST", resp["url"].(string), &b)
  154. if err != nil {
  155. return "", nil, nil, nil, 0, err
  156. }
  157. req.Header.Set("Content-Type", w.FormDataContentType())
  158. req.Header.Set("Origin", "https://web.whatsapp.com")
  159. req.Header.Set("Referer", "https://web.whatsapp.com/")
  160. req.URL.Query().Set("f", "j")
  161. client := &http.Client{}
  162. // Submit the request
  163. res, err := client.Do(req)
  164. if err != nil {
  165. return "", nil, nil, nil, 0, err
  166. }
  167. if res.StatusCode != http.StatusOK {
  168. return "", nil, nil, nil, 0, fmt.Errorf("upload failed with status code %d", res.StatusCode)
  169. }
  170. var jsonRes map[string]string
  171. json.NewDecoder(res.Body).Decode(&jsonRes)
  172. return jsonRes["url"], mediaKey, fileEncSha256, fileSha256, fileLength, nil
  173. }