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 namespace bt::att {
18
AccessRequirements()19 AccessRequirements::AccessRequirements() : value_(0u), min_enc_key_size_(0u) {}
20
AccessRequirements(bool encryption,bool authentication,bool authorization,uint8_t min_enc_key_size)21 AccessRequirements::AccessRequirements(bool encryption,
22 bool authentication,
23 bool authorization,
24 uint8_t min_enc_key_size)
25 : value_(kAttributePermissionBitAllowed),
26 min_enc_key_size_(min_enc_key_size) {
27 if (encryption) {
28 value_ |= kAttributePermissionBitEncryptionRequired;
29 }
30 if (authentication) {
31 value_ |= kAttributePermissionBitAuthenticationRequired;
32 }
33 if (authorization) {
34 value_ |= kAttributePermissionBitAuthorizationRequired;
35 }
36 }
37
Attribute(AttributeGrouping * group,Handle handle,const UUID & type,const AccessRequirements & read_reqs,const AccessRequirements & write_reqs)38 Attribute::Attribute(AttributeGrouping* group,
39 Handle handle,
40 const UUID& type,
41 const AccessRequirements& read_reqs,
42 const AccessRequirements& write_reqs)
43 : group_(group),
44 handle_(handle),
45 type_(type),
46 read_reqs_(read_reqs),
47 write_reqs_(write_reqs) {
48 PW_DCHECK(group_);
49 PW_DCHECK(is_initialized());
50 }
51
Attribute()52 Attribute::Attribute() : handle_(kInvalidHandle) {}
53
SetValue(const ByteBuffer & value)54 void Attribute::SetValue(const ByteBuffer& value) {
55 PW_DCHECK(value.size());
56 PW_DCHECK(value.size() <= kMaxAttributeValueLength);
57 PW_DCHECK(!write_reqs_.allowed());
58 value_ = DynamicByteBuffer(value);
59 }
60
ReadAsync(PeerId peer_id,uint16_t offset,ReadResultCallback result_callback) const61 bool Attribute::ReadAsync(PeerId peer_id,
62 uint16_t offset,
63 ReadResultCallback result_callback) const {
64 if (!is_initialized() || !read_handler_)
65 return false;
66
67 if (!read_reqs_.allowed())
68 return false;
69
70 read_handler_(peer_id, handle_, offset, std::move(result_callback));
71 return true;
72 }
73
WriteAsync(PeerId peer_id,uint16_t offset,const ByteBuffer & value,WriteResultCallback result_callback) const74 bool Attribute::WriteAsync(PeerId peer_id,
75 uint16_t offset,
76 const ByteBuffer& value,
77 WriteResultCallback result_callback) const {
78 if (!is_initialized() || !write_handler_)
79 return false;
80
81 if (!write_reqs_.allowed())
82 return false;
83
84 write_handler_(peer_id, handle_, offset, value, std::move(result_callback));
85 return true;
86 }
87
AttributeGrouping(const UUID & group_type,Handle start_handle,size_t attr_count,const ByteBuffer & decl_value)88 AttributeGrouping::AttributeGrouping(const UUID& group_type,
89 Handle start_handle,
90 size_t attr_count,
91 const ByteBuffer& decl_value)
92 : start_handle_(start_handle), active_(false) {
93 PW_DCHECK(start_handle_ != kInvalidHandle);
94 PW_DCHECK(decl_value.size());
95
96 // It is a programmer error to provide an attr_count which overflows a handle
97 // - this is why the below static cast is OK.
98 PW_CHECK(kHandleMax - start_handle >= attr_count);
99 auto handle_attr_count = static_cast<Handle>(attr_count);
100
101 end_handle_ = start_handle + handle_attr_count;
102 attributes_.reserve(handle_attr_count + 1);
103
104 // TODO(armansito): Allow callers to require at most encryption.
105 attributes_.push_back(Attribute(
106 this,
107 start_handle,
108 group_type,
109 AccessRequirements(/*encryption=*/false,
110 /*authentication=*/false,
111 /*authorization=*/false), // read allowed, no security
112 AccessRequirements())); // write disallowed
113
114 attributes_[0].SetValue(decl_value);
115 }
116
AddAttribute(const UUID & type,const AccessRequirements & read_reqs,const AccessRequirements & write_reqs)117 Attribute* AttributeGrouping::AddAttribute(
118 const UUID& type,
119 const AccessRequirements& read_reqs,
120 const AccessRequirements& write_reqs) {
121 if (complete())
122 return nullptr;
123
124 PW_DCHECK(attributes_[attributes_.size() - 1].handle() < end_handle_);
125
126 // Groupings may not exceed kHandleMax attributes, so if we are incomplete per
127 // the `complete()` check, we necessarily have < kHandleMax attributes. Thus
128 // it is safe to cast attributes_.size() into a Handle.
129 PW_CHECK(attributes_.size() < kHandleMax - start_handle_);
130 Handle handle = start_handle_ + static_cast<Handle>(attributes_.size());
131 attributes_.push_back(Attribute(this, handle, type, read_reqs, write_reqs));
132
133 return &attributes_[handle - start_handle_];
134 }
135
136 } // namespace bt::att
137