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 #include <pw_assert/check.h>
19 #include <pw_preprocessor/compiler.h>
20
21 #include <utility>
22
23 #include "pw_bluetooth_sapphire/internal/host/hci-spec/constants.h"
24 #include "pw_bluetooth_sapphire/internal/host/hci-spec/util.h"
25 #include "pw_bluetooth_sapphire/internal/host/sm/smp.h"
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,bool is_bredr)55 bool HasKeysToDistribute(PairingFeatures features, bool is_bredr) {
56 return DistributableKeys(features.local_key_distribution, is_bredr) ||
57 DistributableKeys(features.remote_key_distribution, is_bredr);
58 }
59
LevelToString(SecurityLevel level)60 const char* LevelToString(SecurityLevel level) {
61 PW_MODIFY_DIAGNOSTICS_PUSH();
62 PW_MODIFY_DIAGNOSTIC(ignored, "-Wswitch-enum");
63 switch (level) {
64 case SecurityLevel::kEncrypted:
65 return "encrypted";
66 case SecurityLevel::kAuthenticated:
67 return "Authenticated";
68 case SecurityLevel::kSecureAuthenticated:
69 return "Authenticated with Secure Connections and 128-bit key";
70 default:
71 break;
72 }
73 PW_MODIFY_DIAGNOSTICS_POP();
74 return "not secure";
75 }
76
SecurityProperties()77 SecurityProperties::SecurityProperties()
78 : SecurityProperties(/*encrypted=*/false,
79 /*authenticated=*/false,
80 /*secure_connections=*/false,
81 0u) {}
82
SecurityProperties(bool encrypted,bool authenticated,bool secure_connections,size_t enc_key_size)83 SecurityProperties::SecurityProperties(bool encrypted,
84 bool authenticated,
85 bool secure_connections,
86 size_t enc_key_size)
87 : properties_(0u), enc_key_size_(enc_key_size) {
88 properties_ |= (encrypted ? Property::kEncrypted : 0u);
89 properties_ |= (authenticated ? Property::kAuthenticated : 0u);
90 properties_ |= (secure_connections ? Property::kSecureConnections : 0u);
91 }
92
SecurityProperties(SecurityLevel level,size_t enc_key_size,bool secure_connections)93 SecurityProperties::SecurityProperties(SecurityLevel level,
94 size_t enc_key_size,
95 bool secure_connections)
96 : SecurityProperties((level >= SecurityLevel::kEncrypted),
97 (level >= SecurityLevel::kAuthenticated),
98 secure_connections,
99 enc_key_size) {}
100 // All BR/EDR link keys, even those from legacy pairing or based on 192-bit EC
101 // points, are stored in 128 bits, according to Core Spec v5.0, Vol 2, Part H
102 // Section 3.1 "Key Types."
SecurityProperties(hci_spec::LinkKeyType lk_type)103 SecurityProperties::SecurityProperties(hci_spec::LinkKeyType lk_type)
104 : SecurityProperties(IsEncryptedKey(lk_type),
105 IsAuthenticatedKey(lk_type),
106 IsSecureConnectionsKey(lk_type),
107 kMaxEncryptionKeySize) {
108 PW_DCHECK(lk_type != hci_spec::LinkKeyType::kChangedCombination,
109 "Can't infer security information from a Changed Combination Key");
110 }
111
SecurityProperties(const SecurityProperties & other)112 SecurityProperties::SecurityProperties(const SecurityProperties& other) {
113 *this = other;
114 }
115
operator =(const SecurityProperties & other)116 SecurityProperties& SecurityProperties::operator=(
117 const SecurityProperties& other) {
118 properties_ = other.properties_;
119 enc_key_size_ = other.enc_key_size_;
120 return *this;
121 }
122
level() const123 SecurityLevel SecurityProperties::level() const {
124 auto level = SecurityLevel::kNoSecurity;
125 if (properties_ & Property::kEncrypted) {
126 level = SecurityLevel::kEncrypted;
127 if (properties_ & Property::kAuthenticated) {
128 level = SecurityLevel::kAuthenticated;
129 if (enc_key_size_ == kMaxEncryptionKeySize &&
130 (properties_ & Property::kSecureConnections)) {
131 level = SecurityLevel::kSecureAuthenticated;
132 }
133 }
134 }
135 return level;
136 }
137
GetLinkKeyType() const138 hci_spec::LinkKeyType SecurityProperties::GetLinkKeyType() const {
139 if (level() == SecurityLevel::kNoSecurity) {
140 // Sapphire considers legacy pairing keys to have security level
141 // kNoSecurity. Returning kCombination type since the kLocalUnit and
142 // kRemoteUnit key types are deprecated.
143 //
144 // TODO(fxbug.dev/42113587): Implement BR/EDR security database
145 return hci_spec::LinkKeyType::kCombination;
146 }
147
148 if (authenticated()) {
149 if (secure_connections()) {
150 return hci_spec::LinkKeyType::kAuthenticatedCombination256;
151 } else {
152 return hci_spec::LinkKeyType::kAuthenticatedCombination192;
153 }
154 } else {
155 if (secure_connections()) {
156 return hci_spec::LinkKeyType::kUnauthenticatedCombination256;
157 } else {
158 return hci_spec::LinkKeyType::kUnauthenticatedCombination192;
159 }
160 }
161 }
162
ToString() const163 std::string SecurityProperties::ToString() const {
164 if (level() == SecurityLevel::kNoSecurity) {
165 return "[no security]";
166 }
167 // inclusive-language: disable
168 return bt_lib_cpp_string::StringPrintf(
169 "[%s%s%skey size: %zu]",
170 encrypted() ? "encrypted " : "",
171 authenticated() ? "authenticated (MITM) " : "",
172 secure_connections() ? "secure connections " : "legacy authentication ",
173 enc_key_size());
174 // inclusive-language: enable
175 }
176
IsAsSecureAs(const SecurityProperties & other) const177 bool SecurityProperties::IsAsSecureAs(const SecurityProperties& other) const {
178 // clang-format off
179 return
180 (encrypted() || !other.encrypted()) &&
181 (authenticated() || !other.authenticated()) &&
182 (secure_connections() || !other.secure_connections()) &&
183 (enc_key_size_ >= other.enc_key_size_);
184 // clang-format on
185 }
186
AttachInspect(inspect::Node & parent,std::string name)187 void SecurityProperties::AttachInspect(inspect::Node& parent,
188 std::string name) {
189 inspect_node_ = parent.CreateChild(name);
190
191 inspect_properties_.level = inspect_node_.CreateString(
192 kInspectLevelPropertyName, LevelToString(level()));
193 inspect_properties_.encrypted =
194 inspect_node_.CreateBool(kInspectEncryptedPropertyName, encrypted());
195 inspect_properties_.secure_connections = inspect_node_.CreateBool(
196 kInspectSecureConnectionsPropertyName, secure_connections());
197 inspect_properties_.authenticated = inspect_node_.CreateBool(
198 kInspectAuthenticatedPropertyName, authenticated());
199 inspect_properties_.key_type = inspect_node_.CreateString(
200 kInspectKeyTypePropertyName,
201 hci_spec::LinkKeyTypeToString(GetLinkKeyType()));
202 }
203
LTK(const SecurityProperties & security,const hci_spec::LinkKey & key)204 LTK::LTK(const SecurityProperties& security, const hci_spec::LinkKey& key)
205 : security_(security), key_(key) {}
206
AttachInspect(inspect::Node & parent,std::string name)207 void LTK::AttachInspect(inspect::Node& parent, std::string name) {
208 security_.AttachInspect(parent, std::move(name));
209 }
210
Key(const SecurityProperties & security,const UInt128 & value)211 Key::Key(const SecurityProperties& security, const UInt128& value)
212 : security_(security), value_(value) {}
213
214 } // namespace bt::sm
215