rtsp_crypto.c 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. #include "rtsp_crypto.h"
  2. #include <errno.h>
  3. #include <stdlib.h>
  4. #include <string.h>
  5. #include <sys/socket.h>
  6. #include "esp_log.h"
  7. #include "sodium.h"
  8. static const char *TAG = "rtsp_crypto";
  9. // Send all data, handling partial sends
  10. static int send_all(int socket, const uint8_t *data, size_t len) {
  11. size_t sent = 0;
  12. while (sent < len) {
  13. ssize_t r = send(socket, data + sent, len - sent, 0);
  14. if (r <= 0) {
  15. return -1;
  16. }
  17. sent += (size_t)r;
  18. }
  19. return 0;
  20. }
  21. int rtsp_crypto_read_block(int socket, rtsp_conn_t *conn, uint8_t *buffer,
  22. size_t buffer_size) {
  23. if (!conn || !conn->hap_session || !conn->encrypted_mode) {
  24. // Expected during session teardown - not an error
  25. return -1;
  26. }
  27. // Read 2-byte length header (little-endian). The socket has SO_RCVTIMEO
  28. // set for shutdown responsiveness; if it fires after a partial read we
  29. // must keep waiting rather than return — abandoning N consumed bytes here
  30. // permanently desyncs the encrypted stream framing and turns every later
  31. // recv into garbage interpreted as a fresh length prefix. Cancellation
  32. // is handled by shutdown(SHUT_RDWR), which makes recv return a real error.
  33. uint8_t len_buf[2];
  34. size_t received = 0;
  35. while (received < 2) {
  36. ssize_t r = recv(socket, len_buf + received, 2 - received, 0);
  37. if (r > 0) {
  38. received += (size_t)r;
  39. continue;
  40. }
  41. if (r == 0) {
  42. return -1; // peer closed
  43. }
  44. if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) {
  45. continue;
  46. }
  47. return -1;
  48. }
  49. uint16_t block_len = (uint16_t)len_buf[0] | ((uint16_t)len_buf[1] << 8);
  50. if (block_len == 0 || block_len > RTSP_ENCRYPTED_BLOCK_MAX ||
  51. block_len > buffer_size) {
  52. ESP_LOGE(TAG, "Invalid encrypted block length: %d", block_len);
  53. return -1;
  54. }
  55. // Allocate temporary buffer for encrypted data
  56. size_t encrypted_len = block_len + 16; // +16 for Poly1305 tag
  57. uint8_t *encrypted = malloc(encrypted_len);
  58. if (!encrypted) {
  59. ESP_LOGE(TAG, "Failed to allocate encrypted buffer");
  60. return -1;
  61. }
  62. received = 0;
  63. while (received < encrypted_len) {
  64. ssize_t r = recv(socket, encrypted + received, encrypted_len - received, 0);
  65. if (r > 0) {
  66. received += (size_t)r;
  67. continue;
  68. }
  69. if (r == 0) {
  70. free(encrypted);
  71. return -1; // peer closed
  72. }
  73. if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) {
  74. continue;
  75. }
  76. free(encrypted);
  77. return -1;
  78. }
  79. // Decrypt using session keys
  80. uint8_t nonce[12] = {0};
  81. memcpy(nonce + 4, &conn->hap_session->decrypt_nonce, 8);
  82. unsigned long long plaintext_len;
  83. if (crypto_aead_chacha20poly1305_ietf_decrypt(
  84. buffer, &plaintext_len, NULL, encrypted, encrypted_len, len_buf,
  85. sizeof(len_buf), nonce, conn->hap_session->decrypt_key) != 0) {
  86. free(encrypted);
  87. ESP_LOGE(TAG, "Failed to decrypt frame");
  88. return -1;
  89. }
  90. free(encrypted);
  91. conn->hap_session->decrypt_nonce++;
  92. return (int)plaintext_len;
  93. }
  94. int rtsp_crypto_write_frame(int socket, rtsp_conn_t *conn, const uint8_t *data,
  95. size_t data_len) {
  96. if (!conn || !conn->hap_session || !conn->encrypted_mode) {
  97. // Expected during session teardown - not an error
  98. return -1;
  99. }
  100. size_t offset = 0;
  101. while (offset < data_len) {
  102. uint16_t block_len = (data_len - offset) > RTSP_ENCRYPTED_BLOCK_MAX
  103. ? RTSP_ENCRYPTED_BLOCK_MAX
  104. : (uint16_t)(data_len - offset);
  105. uint8_t len_buf[2];
  106. len_buf[0] = block_len & 0xFF;
  107. len_buf[1] = (block_len >> 8) & 0xFF;
  108. uint8_t nonce[12] = {0};
  109. memcpy(nonce + 4, &conn->hap_session->encrypt_nonce, 8);
  110. size_t encrypted_len = block_len + 16; // +16 for Poly1305 tag
  111. uint8_t *encrypted = malloc(encrypted_len);
  112. if (!encrypted) {
  113. ESP_LOGE(TAG, "Failed to allocate encrypted buffer");
  114. return -1;
  115. }
  116. unsigned long long ct_len;
  117. crypto_aead_chacha20poly1305_ietf_encrypt(
  118. encrypted, &ct_len, data + offset, block_len, len_buf, sizeof(len_buf),
  119. NULL, nonce, conn->hap_session->encrypt_key);
  120. if (ct_len != encrypted_len) {
  121. ESP_LOGE(TAG, "Unexpected encrypted length: %llu", ct_len);
  122. free(encrypted);
  123. return -1;
  124. }
  125. if (send_all(socket, len_buf, sizeof(len_buf)) != 0 ||
  126. send_all(socket, encrypted, encrypted_len) != 0) {
  127. ESP_LOGE(TAG, "Failed to send encrypted block");
  128. free(encrypted);
  129. return -1;
  130. }
  131. free(encrypted);
  132. conn->hap_session->encrypt_nonce++;
  133. offset += block_len;
  134. }
  135. return 0;
  136. }