bap_frame.cpp 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. #include <algorithm>
  2. #include <limits>
  3. #include "../encoding/byte_order.h"
  4. #include "frame.h"
  5. namespace sunrise::middleware::bap {
  6. namespace {
  7. /** Protocol marker byte 1 starts every BAP outer frame, sent and received. */
  8. constexpr std::byte kMagic{0x01};
  9. /** Fixed wire offsets for the 6-byte BAP outer header. */
  10. constexpr std::size_t kOuterMagicOffset = 0;
  11. constexpr std::size_t kOuterTypeOffset = 1;
  12. constexpr std::size_t kOuterLengthOffset = 2;
  13. constexpr std::size_t kOuterHeaderSize = kOuterLengthOffset + encoding::kU32Size;
  14. /** Fixed wire offsets for the 6-byte request and 8-byte response headers. */
  15. constexpr std::size_t kServiceOffset = 0;
  16. constexpr std::size_t kTaskOffset = kServiceOffset + encoding::kU16Size;
  17. constexpr std::size_t kRequestHeaderSize = kTaskOffset + encoding::kU32Size;
  18. constexpr std::size_t kStatusOffset = kRequestHeaderSize;
  19. constexpr std::size_t kResponseHeaderSize = kStatusOffset + encoding::kU16Size;
  20. /** The BAP response status the Client reads as success. */
  21. constexpr std::uint16_t kStatusOk = 200;
  22. /** @return True for either plaintext BAP frame-type value. */
  23. [[nodiscard]] bool is_plaintext(FrameType frameType) noexcept {
  24. return frameType == FrameType::plaintext0 || frameType == FrameType::plaintext2;
  25. }
  26. } // namespace
  27. /** Reads one whole BAP outer frame and borrows the payload it declares. */
  28. bool parse_frame(std::span<const std::byte> input, OuterFrame& frame) noexcept {
  29. frame = {};
  30. if (input.size() < kOuterHeaderSize || input[kOuterMagicOffset] != kMagic) {
  31. return false;
  32. }
  33. const std::size_t payloadSize =
  34. encoding::read_u32_be(input.subspan<kOuterLengthOffset, encoding::kU32Size>());
  35. // Bytes past the declared payload are outside its authentication tag, so a frame carrying
  36. // them is not the frame the peer signed.
  37. if (payloadSize != input.size() - kOuterHeaderSize) {
  38. return false;
  39. }
  40. frame.frameType =
  41. static_cast<FrameType>(std::to_integer<std::uint8_t>(input[kOuterTypeOffset]));
  42. frame.payload = input.subspan(kOuterHeaderSize, payloadSize);
  43. return true;
  44. }
  45. /** Parses a decrypted or plaintext BAP request payload. */
  46. bool parse_request_payload(std::span<const std::byte> input,
  47. FrameType frameType,
  48. RequestFrame& request) noexcept {
  49. request = {};
  50. if (input.size() < kRequestHeaderSize) {
  51. return false;
  52. }
  53. request.frameType = frameType;
  54. request.serviceId = encoding::read_u16_be(input.subspan<kServiceOffset, encoding::kU16Size>());
  55. request.taskId = encoding::read_u32_be(input.subspan<kTaskOffset, encoding::kU32Size>());
  56. request.body = input.subspan(kRequestHeaderSize);
  57. return true;
  58. }
  59. /** Parses a complete plaintext BAP request. */
  60. bool parse_request(std::span<const std::byte> input, RequestFrame& request) noexcept {
  61. OuterFrame outer;
  62. if (!parse_frame(input, outer) || !is_plaintext(outer.frameType)) {
  63. request = {};
  64. return false;
  65. }
  66. return parse_request_payload(outer.payload, outer.frameType, request);
  67. }
  68. /** Encodes one status-200 BAP response header and body. */
  69. bool encode_response_payload(ResponseService service,
  70. std::uint32_t taskId,
  71. std::span<const std::byte> body,
  72. std::span<std::byte> output,
  73. std::size_t& written) noexcept {
  74. written = 0;
  75. if (body.size() > std::numeric_limits<std::uint32_t>::max() - kResponseHeaderSize
  76. || output.size() < kResponseHeaderSize + body.size()) {
  77. return false;
  78. }
  79. encoding::write_u16_be(output.subspan<kServiceOffset, encoding::kU16Size>(),
  80. static_cast<std::uint16_t>(service));
  81. encoding::write_u32_be(output.subspan<kTaskOffset, encoding::kU32Size>(), taskId);
  82. encoding::write_u16_be(output.subspan<kStatusOffset, encoding::kU16Size>(), kStatusOk);
  83. std::copy(body.begin(), body.end(), output.begin() + kResponseHeaderSize);
  84. written = kResponseHeaderSize + body.size();
  85. return true;
  86. }
  87. /** Encodes a server notification with the smallest 6-byte inner header. */
  88. bool encode_notification_payload(NotificationService service,
  89. std::uint32_t sequence,
  90. std::span<const std::byte> body,
  91. std::span<std::byte> output,
  92. std::size_t& written) noexcept {
  93. written = 0;
  94. if (body.size() > std::numeric_limits<std::uint32_t>::max() - kRequestHeaderSize
  95. || output.size() < kRequestHeaderSize + body.size()) {
  96. return false;
  97. }
  98. encoding::write_u16_be(output.subspan<kServiceOffset, encoding::kU16Size>(),
  99. static_cast<std::uint16_t>(service));
  100. encoding::write_u32_be(output.subspan<kTaskOffset, encoding::kU32Size>(), sequence);
  101. std::copy(body.begin(), body.end(), output.begin() + kRequestHeaderSize);
  102. written = kRequestHeaderSize + body.size();
  103. return true;
  104. }
  105. /** Encodes one BAP outer header around an existing payload. */
  106. bool encode_frame(FrameType frameType,
  107. std::span<const std::byte> payload,
  108. std::span<std::byte> output,
  109. std::size_t& written) noexcept {
  110. written = 0;
  111. if (payload.size() > std::numeric_limits<std::uint32_t>::max()
  112. || output.size() < kOuterHeaderSize + payload.size()) {
  113. return false;
  114. }
  115. output[kOuterMagicOffset] = kMagic;
  116. output[kOuterTypeOffset] = static_cast<std::byte>(frameType);
  117. encoding::write_u32_be(output.subspan<kOuterLengthOffset, encoding::kU32Size>(),
  118. static_cast<std::uint32_t>(payload.size()));
  119. // Payload may already sit at its final offset inside output, so copy element by element.
  120. for (std::size_t index = 0; index < payload.size(); ++index) {
  121. output[kOuterHeaderSize + index] = payload[index];
  122. }
  123. written = kOuterHeaderSize + payload.size();
  124. return true;
  125. }
  126. /** Encodes one complete plaintext status-200 BAP response. */
  127. bool encode_response(ResponseService service,
  128. std::uint32_t taskId,
  129. FrameType frameType,
  130. std::span<const std::byte> body,
  131. std::span<std::byte> output,
  132. std::size_t& written) noexcept {
  133. written = 0;
  134. if (!is_plaintext(frameType) || output.size() < kOuterHeaderSize) {
  135. return false;
  136. }
  137. std::size_t payloadSize = 0;
  138. if (!encode_response_payload(
  139. service, taskId, body, output.subspan(kOuterHeaderSize), payloadSize)) {
  140. return false;
  141. }
  142. return encode_frame(frameType, output.subspan(kOuterHeaderSize, payloadSize), output, written);
  143. }
  144. } // namespace sunrise::middleware::bap