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