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/sm/types.h"
16
17 #include <cpp-string/string_printf.h>
18
19 #include <utility>
20
21 #include "pw_bluetooth_sapphire/internal/host/hci-spec/constants.h"
22 #include "pw_bluetooth_sapphire/internal/host/hci-spec/util.h"
23 #include "pw_bluetooth_sapphire/internal/host/sm/smp.h"
24
25 #pragma clang diagnostic ignored "-Wswitch-enum"
26
27 namespace bt::sm {
28 namespace {
29 const char* const kInspectLevelPropertyName = "level";
30 const char* const kInspectEncryptedPropertyName = "encrypted";
31 const char* const kInspectSecureConnectionsPropertyName = "secure_connections";
32 const char* const kInspectAuthenticatedPropertyName = "authenticated";
33 const char* const kInspectKeyTypePropertyName = "key_type";
34
IsEncryptedKey(hci_spec::LinkKeyType lk_type)35 bool IsEncryptedKey(hci_spec::LinkKeyType lk_type) {
36 return (lk_type == hci_spec::LinkKeyType::kDebugCombination ||
37 lk_type == hci_spec::LinkKeyType::kUnauthenticatedCombination192 ||
38 lk_type == hci_spec::LinkKeyType::kUnauthenticatedCombination256 ||
39 lk_type == hci_spec::LinkKeyType::kAuthenticatedCombination192 ||
40 lk_type == hci_spec::LinkKeyType::kAuthenticatedCombination256);
41 }
42
IsAuthenticatedKey(hci_spec::LinkKeyType lk_type)43 bool IsAuthenticatedKey(hci_spec::LinkKeyType lk_type) {
44 return (lk_type == hci_spec::LinkKeyType::kAuthenticatedCombination192 ||
45 lk_type == hci_spec::LinkKeyType::kAuthenticatedCombination256);
46 }
47
IsSecureConnectionsKey(hci_spec::LinkKeyType lk_type)48 bool IsSecureConnectionsKey(hci_spec::LinkKeyType lk_type) {
49 return (lk_type == hci_spec::LinkKeyType::kUnauthenticatedCombination256 ||
50 lk_type == hci_spec::LinkKeyType::kAuthenticatedCombination256);
51 }
52
53 } // namespace
54
HasKeysToDistribute(PairingFeatures features)55 bool HasKeysToDistribute(PairingFeatures features) {
56 return DistributableKeys(features.local_key_distribution) ||
57 DistributableKeys(features.remote_key_distribution);
58 }
59
LevelToString(SecurityLevel level)60 const char* LevelToString(SecurityLevel level) {
61 switch (level) {
62 case SecurityLevel::kEncrypted:
63 return "encrypted";
64 case SecurityLevel::kAuthenticated:
65 return "Authenticated";
66 case SecurityLevel::kSecureAuthenticated:
67 return "Authenticated with Secure Connections and 128-bit key";
68 default:
69 break;
70 }
71 return "not secure";
72 }
73
SecurityProperties()74 SecurityProperties::SecurityProperties()
75 : SecurityProperties(/*encrypted=*/false,
76 /*authenticated=*/false,
77 /*secure_connections=*/false,
78 0u) {}
79
SecurityProperties(bool encrypted,bool authenticated,bool secure_connections,size_t enc_key_size)80 SecurityProperties::SecurityProperties(bool encrypted,
81 bool authenticated,
82 bool secure_connections,
83 size_t enc_key_size)
84 : properties_(0u), enc_key_size_(enc_key_size) {
85 properties_ |= (encrypted ? Property::kEncrypted : 0u);
86 properties_ |= (authenticated ? Property::kAuthenticated : 0u);
87 properties_ |= (secure_connections ? Property::kSecureConnections : 0u);
88 }
89
SecurityProperties(SecurityLevel level,size_t enc_key_size,bool secure_connections)90 SecurityProperties::SecurityProperties(SecurityLevel level,
91 size_t enc_key_size,
92 bool secure_connections)
93 : SecurityProperties((level >= SecurityLevel::kEncrypted),
94 (level >= SecurityLevel::kAuthenticated),
95 secure_connections,
96 enc_key_size) {}
97 // All BR/EDR link keys, even those from legacy pairing or based on 192-bit EC
98 // points, are stored in 128 bits, according to Core Spec v5.0, Vol 2, Part H
99 // Section 3.1 "Key Types."
SecurityProperties(hci_spec::LinkKeyType lk_type)100 SecurityProperties::SecurityProperties(hci_spec::LinkKeyType lk_type)
101 : SecurityProperties(IsEncryptedKey(lk_type),
102 IsAuthenticatedKey(lk_type),
103 IsSecureConnectionsKey(lk_type),
104 kMaxEncryptionKeySize) {
105 BT_DEBUG_ASSERT_MSG(
106 lk_type != hci_spec::LinkKeyType::kChangedCombination,
107 "Can't infer security information from a Changed Combination Key");
108 }
109
SecurityProperties(const SecurityProperties & other)110 SecurityProperties::SecurityProperties(const SecurityProperties& other) {
111 *this = other;
112 }
113
operator =(const SecurityProperties & other)114 SecurityProperties& SecurityProperties::operator=(
115 const SecurityProperties& other) {
116 properties_ = other.properties_;
117 enc_key_size_ = other.enc_key_size_;
118 return *this;
119 }
120
level() const121 SecurityLevel SecurityProperties::level() const {
122 auto level = SecurityLevel::kNoSecurity;
123 if (properties_ & Property::kEncrypted) {
124 level = SecurityLevel::kEncrypted;
125 if (properties_ & Property::kAuthenticated) {
126 level = SecurityLevel::kAuthenticated;
127 if (enc_key_size_ == kMaxEncryptionKeySize &&
128 (properties_ & Property::kSecureConnections)) {
129 level = SecurityLevel::kSecureAuthenticated;
130 }
131 }
132 }
133 return level;
134 }
135
GetLinkKeyType() const136 std::optional<hci_spec::LinkKeyType> SecurityProperties::GetLinkKeyType()
137 const {
138 if (level() == SecurityLevel::kNoSecurity) {
139 return std::nullopt;
140 }
141 if (authenticated()) {
142 if (secure_connections()) {
143 return hci_spec::LinkKeyType::kAuthenticatedCombination256;
144 } else {
145 return hci_spec::LinkKeyType::kAuthenticatedCombination192;
146 }
147 } else {
148 if (secure_connections()) {
149 return hci_spec::LinkKeyType::kUnauthenticatedCombination256;
150 } else {
151 return hci_spec::LinkKeyType::kUnauthenticatedCombination192;
152 }
153 }
154 }
155
ToString() const156 std::string SecurityProperties::ToString() const {
157 if (level() == SecurityLevel::kNoSecurity) {
158 return "[no security]";
159 }
160 // inclusive-language: disable
161 return bt_lib_cpp_string::StringPrintf(
162 "[%s%s%skey size: %lu]",
163 encrypted() ? "encrypted " : "",
164 authenticated() ? "authenticated (MITM) " : "",
165 secure_connections() ? "secure connections " : "legacy authentication ",
166 enc_key_size());
167 // inclusive-language: enable
168 }
169
IsAsSecureAs(const SecurityProperties & other) const170 bool SecurityProperties::IsAsSecureAs(const SecurityProperties& other) const {
171 // clang-format off
172 return
173 (encrypted() || !other.encrypted()) &&
174 (authenticated() || !other.authenticated()) &&
175 (secure_connections() || !other.secure_connections()) &&
176 (enc_key_size_ >= other.enc_key_size_);
177 // clang-format on
178 }
179
AttachInspect(inspect::Node & parent,std::string name)180 void SecurityProperties::AttachInspect(inspect::Node& parent,
181 std::string name) {
182 inspect_node_ = parent.CreateChild(name);
183
184 inspect_properties_.level = inspect_node_.CreateString(
185 kInspectLevelPropertyName, LevelToString(level()));
186 inspect_properties_.encrypted =
187 inspect_node_.CreateBool(kInspectEncryptedPropertyName, encrypted());
188 inspect_properties_.secure_connections = inspect_node_.CreateBool(
189 kInspectSecureConnectionsPropertyName, secure_connections());
190 inspect_properties_.authenticated = inspect_node_.CreateBool(
191 kInspectAuthenticatedPropertyName, authenticated());
192 if (GetLinkKeyType().has_value()) {
193 inspect_properties_.key_type = inspect_node_.CreateString(
194 kInspectKeyTypePropertyName,
195 hci_spec::LinkKeyTypeToString(GetLinkKeyType().value()));
196 }
197 }
198
LTK(const SecurityProperties & security,const hci_spec::LinkKey & key)199 LTK::LTK(const SecurityProperties& security, const hci_spec::LinkKey& key)
200 : security_(security), key_(key) {}
201
AttachInspect(inspect::Node & parent,std::string name)202 void LTK::AttachInspect(inspect::Node& parent, std::string name) {
203 security_.AttachInspect(parent, std::move(name));
204 }
205
Key(const SecurityProperties & security,const UInt128 & value)206 Key::Key(const SecurityProperties& security, const UInt128& value)
207 : security_(security), value_(value) {}
208
209 } // namespace bt::sm
210