1 // Copyright 2023 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14
15 #include "pw_bluetooth_sapphire/internal/host/att/attribute.h"
16
17 #include <pw_assert/check.h>
18
19 namespace bt::att {
20
AccessRequirements()21 AccessRequirements::AccessRequirements() : value_(0u), min_enc_key_size_(0u) {}
22
AccessRequirements(bool encryption,bool authentication,bool authorization,uint8_t min_enc_key_size)23 AccessRequirements::AccessRequirements(bool encryption,
24 bool authentication,
25 bool authorization,
26 uint8_t min_enc_key_size)
27 : value_(kAttributePermissionBitAllowed),
28 min_enc_key_size_(min_enc_key_size) {
29 if (encryption) {
30 value_ |= kAttributePermissionBitEncryptionRequired;
31 }
32 if (authentication) {
33 value_ |= kAttributePermissionBitAuthenticationRequired;
34 }
35 if (authorization) {
36 value_ |= kAttributePermissionBitAuthorizationRequired;
37 }
38 }
39
Attribute(AttributeGrouping * group,Handle handle,const UUID & type,const AccessRequirements & read_reqs,const AccessRequirements & write_reqs)40 Attribute::Attribute(AttributeGrouping* group,
41 Handle handle,
42 const UUID& type,
43 const AccessRequirements& read_reqs,
44 const AccessRequirements& write_reqs)
45 : group_(group),
46 handle_(handle),
47 type_(type),
48 read_reqs_(read_reqs),
49 write_reqs_(write_reqs) {
50 PW_DCHECK(group_);
51 PW_DCHECK(is_initialized());
52 }
53
Attribute()54 Attribute::Attribute() : handle_(kInvalidHandle) {}
55
SetValue(const ByteBuffer & value)56 void Attribute::SetValue(const ByteBuffer& value) {
57 PW_DCHECK(value.size());
58 PW_DCHECK(value.size() <= kMaxAttributeValueLength);
59 PW_DCHECK(!write_reqs_.allowed());
60 value_ = DynamicByteBuffer(value);
61 }
62
ReadAsync(PeerId peer_id,uint16_t offset,ReadResultCallback result_callback) const63 bool Attribute::ReadAsync(PeerId peer_id,
64 uint16_t offset,
65 ReadResultCallback result_callback) const {
66 if (!is_initialized() || !read_handler_)
67 return false;
68
69 if (!read_reqs_.allowed())
70 return false;
71
72 read_handler_(peer_id, handle_, offset, std::move(result_callback));
73 return true;
74 }
75
WriteAsync(PeerId peer_id,uint16_t offset,const ByteBuffer & value,WriteResultCallback result_callback) const76 bool Attribute::WriteAsync(PeerId peer_id,
77 uint16_t offset,
78 const ByteBuffer& value,
79 WriteResultCallback result_callback) const {
80 if (!is_initialized() || !write_handler_)
81 return false;
82
83 if (!write_reqs_.allowed())
84 return false;
85
86 write_handler_(peer_id, handle_, offset, value, std::move(result_callback));
87 return true;
88 }
89
AttributeGrouping(const UUID & group_type,Handle start_handle,size_t attr_count,const ByteBuffer & decl_value)90 AttributeGrouping::AttributeGrouping(const UUID& group_type,
91 Handle start_handle,
92 size_t attr_count,
93 const ByteBuffer& decl_value)
94 : start_handle_(start_handle), active_(false) {
95 PW_DCHECK(start_handle_ != kInvalidHandle);
96 PW_DCHECK(decl_value.size());
97
98 // It is a programmer error to provide an attr_count which overflows a handle
99 // - this is why the below static cast is OK.
100 PW_CHECK(kHandleMax - start_handle >= attr_count);
101 auto handle_attr_count = static_cast<Handle>(attr_count);
102
103 end_handle_ = start_handle + handle_attr_count;
104 attributes_.reserve(handle_attr_count + 1);
105
106 // TODO(armansito): Allow callers to require at most encryption.
107 attributes_.push_back(Attribute(
108 this,
109 start_handle,
110 group_type,
111 AccessRequirements(/*encryption=*/false,
112 /*authentication=*/false,
113 /*authorization=*/false), // read allowed, no security
114 AccessRequirements())); // write disallowed
115
116 attributes_[0].SetValue(decl_value);
117 }
118
AddAttribute(const UUID & type,const AccessRequirements & read_reqs,const AccessRequirements & write_reqs)119 Attribute* AttributeGrouping::AddAttribute(
120 const UUID& type,
121 const AccessRequirements& read_reqs,
122 const AccessRequirements& write_reqs) {
123 if (complete())
124 return nullptr;
125
126 PW_DCHECK(attributes_[attributes_.size() - 1].handle() < end_handle_);
127
128 // Groupings may not exceed kHandleMax attributes, so if we are incomplete per
129 // the `complete()` check, we necessarily have < kHandleMax attributes. Thus
130 // it is safe to cast attributes_.size() into a Handle.
131 PW_CHECK(attributes_.size() < kHandleMax - start_handle_);
132 Handle handle = start_handle_ + static_cast<Handle>(attributes_.size());
133 attributes_.push_back(Attribute(this, handle, type, read_reqs, write_reqs));
134
135 return &attributes_[handle - start_handle_];
136 }
137
138 } // namespace bt::att
139