Line data Source code
1 : /*
2 : * Copyright (c) 2022 Project CHIP Authors
3 : * All rights reserved.
4 : *
5 : * Licensed under the Apache License, Version 2.0 (the "License");
6 : * you may not use this file except in compliance with the License.
7 : * You may obtain a copy of the License at
8 : *
9 : * http://www.apache.org/licenses/LICENSE-2.0
10 : *
11 : * Unless required by applicable law or agreed to in writing, software
12 : * distributed under the License is distributed on an "AS IS" BASIS,
13 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 : * See the License for the specific language governing permissions and
15 : * limitations under the License.
16 : */
17 :
18 : #include <protocols/secure_channel/DefaultSessionResumptionStorage.h>
19 :
20 : #include <lib/support/Base64.h>
21 : #include <lib/support/SafeInt.h>
22 :
23 : namespace chip {
24 :
25 312 : CHIP_ERROR DefaultSessionResumptionStorage::FindByScopedNodeId(const ScopedNodeId & node, ResumptionIdStorage & resumptionId,
26 : Crypto::P256ECDHDerivedSecret & sharedSecret, CATValues & peerCATs)
27 : {
28 312 : ReturnErrorOnFailure(LoadState(node, resumptionId, sharedSecret, peerCATs));
29 158 : return CHIP_NO_ERROR;
30 : }
31 :
32 161 : CHIP_ERROR DefaultSessionResumptionStorage::FindByResumptionId(ConstResumptionIdView resumptionId, ScopedNodeId & node,
33 : Crypto::P256ECDHDerivedSecret & sharedSecret, CATValues & peerCATs)
34 : {
35 161 : ReturnErrorOnFailure(FindNodeByResumptionId(resumptionId, node));
36 : ResumptionIdStorage tmpResumptionId;
37 55 : ReturnErrorOnFailure(FindByScopedNodeId(node, tmpResumptionId, sharedSecret, peerCATs));
38 55 : VerifyOrReturnError(std::equal(tmpResumptionId.begin(), tmpResumptionId.end(), resumptionId.begin(), resumptionId.end()),
39 : CHIP_ERROR_KEY_NOT_FOUND);
40 55 : return CHIP_NO_ERROR;
41 : }
42 :
43 161 : CHIP_ERROR DefaultSessionResumptionStorage::FindNodeByResumptionId(ConstResumptionIdView resumptionId, ScopedNodeId & node)
44 : {
45 161 : ReturnErrorOnFailure(LoadLink(resumptionId, node));
46 55 : return CHIP_NO_ERROR;
47 : }
48 :
49 206 : CHIP_ERROR DefaultSessionResumptionStorage::Save(const ScopedNodeId & node, ConstResumptionIdView resumptionId,
50 : const Crypto::P256ECDHDerivedSecret & sharedSecret, const CATValues & peerCATs)
51 : {
52 206 : SessionIndex index;
53 206 : ReturnErrorOnFailure(LoadIndex(index));
54 :
55 3698 : for (size_t i = 0; i < index.mSize; ++i)
56 : {
57 3550 : if (index.mNodes[i] == node)
58 : {
59 : // Node already exists in the index. Save in place.
60 58 : CHIP_ERROR err = CHIP_NO_ERROR;
61 : ResumptionIdStorage oldResumptionId;
62 58 : Crypto::P256ECDHDerivedSecret oldSharedSecret;
63 58 : CATValues oldPeerCATs;
64 : // This follows the approach in Delete. Removal of the old
65 : // resumption-id-keyed link is best effort. If we cannot load
66 : // state to lookup the resumption ID for the key, the entry in
67 : // the link table will be leaked.
68 58 : err = LoadState(node, oldResumptionId, oldSharedSecret, oldPeerCATs);
69 116 : if (err != CHIP_NO_ERROR)
70 : {
71 0 : ChipLogError(SecureChannel,
72 : "LoadState failed; unable to fully delete session resumption record for node " ChipLogFormatX64
73 : ": %" CHIP_ERROR_FORMAT,
74 : ChipLogValueX64(node.GetNodeId()), err.Format());
75 : }
76 : else
77 : {
78 58 : err = DeleteLink(oldResumptionId);
79 116 : if (err != CHIP_NO_ERROR)
80 : {
81 0 : ChipLogError(SecureChannel,
82 : "DeleteLink failed; unable to fully delete session resumption record for node " ChipLogFormatX64
83 : ": %" CHIP_ERROR_FORMAT,
84 : ChipLogValueX64(node.GetNodeId()), err.Format());
85 : }
86 : }
87 58 : ReturnErrorOnFailure(SaveState(node, resumptionId, sharedSecret, peerCATs));
88 58 : ReturnErrorOnFailure(SaveLink(resumptionId, node));
89 58 : return CHIP_NO_ERROR;
90 58 : }
91 : }
92 :
93 : // Constant product inside a CHIPConfig.h macro; cannot widen at the use site.
94 : // NOLINTNEXTLINE(bugprone-implicit-widening-of-multiplication-result)
95 148 : if (index.mSize == CHIP_CONFIG_CASE_SESSION_RESUME_CACHE_SIZE)
96 : {
97 : // TODO: implement LRU for resumption
98 1 : ReturnErrorOnFailure(Delete(index.mNodes[0]));
99 1 : ReturnErrorOnFailure(LoadIndex(index));
100 : }
101 :
102 148 : ReturnErrorOnFailure(SaveState(node, resumptionId, sharedSecret, peerCATs));
103 148 : ReturnErrorOnFailure(SaveLink(resumptionId, node));
104 :
105 148 : index.mNodes[index.mSize++] = node;
106 148 : ReturnErrorOnFailure(SaveIndex(index));
107 :
108 148 : return CHIP_NO_ERROR;
109 : }
110 :
111 49 : CHIP_ERROR DefaultSessionResumptionStorage::Delete(const ScopedNodeId & node)
112 : {
113 49 : SessionIndex index;
114 49 : ReturnErrorOnFailure(LoadIndex(index));
115 :
116 : ResumptionIdStorage resumptionId;
117 49 : Crypto::P256ECDHDerivedSecret sharedSecret;
118 49 : CATValues peerCATs;
119 49 : CHIP_ERROR err = LoadState(node, resumptionId, sharedSecret, peerCATs);
120 98 : if (err == CHIP_NO_ERROR)
121 : {
122 49 : err = DeleteLink(resumptionId);
123 98 : if (err != CHIP_NO_ERROR && err != CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND)
124 : {
125 0 : ChipLogError(SecureChannel,
126 : "Unable to delete session resumption link for node " ChipLogFormatX64 ": %" CHIP_ERROR_FORMAT,
127 : ChipLogValueX64(node.GetNodeId()), err.Format());
128 : }
129 : }
130 0 : else if (err != CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND)
131 : {
132 0 : ChipLogError(SecureChannel,
133 : "Unable to load session resumption state during session deletion for node " ChipLogFormatX64
134 : ": %" CHIP_ERROR_FORMAT,
135 : ChipLogValueX64(node.GetNodeId()), err.Format());
136 : }
137 :
138 49 : err = DeleteState(node);
139 98 : if (err != CHIP_NO_ERROR && err != CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND)
140 : {
141 0 : ChipLogError(SecureChannel, "Unable to delete session resumption state for node " ChipLogFormatX64 ": %" CHIP_ERROR_FORMAT,
142 : ChipLogValueX64(node.GetNodeId()), err.Format());
143 : }
144 :
145 49 : bool found = false;
146 1225 : for (size_t i = 0; i < index.mSize; ++i)
147 : {
148 1176 : if (found)
149 : {
150 : // index.mSize was decreased by 1 when found was set to true.
151 : // So the (i+1)th element isn't out of bounds.
152 1127 : index.mNodes[i] = index.mNodes[i + 1];
153 : }
154 : else
155 : {
156 49 : if (index.mNodes[i] == node)
157 : {
158 49 : found = true;
159 49 : if (i + 1 < index.mSize)
160 : {
161 48 : index.mNodes[i] = index.mNodes[i + 1];
162 : }
163 49 : index.mSize -= 1;
164 : }
165 : }
166 : }
167 :
168 49 : if (found)
169 : {
170 49 : err = SaveIndex(index);
171 98 : if (err != CHIP_NO_ERROR)
172 : {
173 0 : ChipLogError(SecureChannel, "Unable to save session resumption index: %" CHIP_ERROR_FORMAT, err.Format());
174 : }
175 : }
176 : else
177 : {
178 0 : ChipLogError(SecureChannel,
179 : "Unable to find session resumption state for node in index" ChipLogFormatX64 ": %" CHIP_ERROR_FORMAT,
180 : ChipLogValueX64(node.GetNodeId()), err.Format());
181 : }
182 :
183 49 : return CHIP_NO_ERROR;
184 49 : }
185 :
186 19 : CHIP_ERROR DefaultSessionResumptionStorage::DeleteAll(FabricIndex fabricIndex)
187 : {
188 19 : CHIP_ERROR stickyErr = CHIP_NO_ERROR;
189 19 : size_t found = 0;
190 19 : SessionIndex index;
191 19 : ReturnErrorOnFailure(LoadIndex(index));
192 19 : size_t initialSize = index.mSize;
193 433 : for (size_t i = 0; i < initialSize; ++i)
194 : {
195 414 : CHIP_ERROR err = CHIP_NO_ERROR;
196 414 : size_t cur = i - found;
197 414 : size_t remain = initialSize - i;
198 : ResumptionIdStorage resumptionId;
199 414 : Crypto::P256ECDHDerivedSecret sharedSecret;
200 414 : CATValues peerCATs;
201 414 : if (index.mNodes[cur].GetFabricIndex() != fabricIndex)
202 : {
203 363 : continue;
204 : }
205 51 : err = LoadState(index.mNodes[cur], resumptionId, sharedSecret, peerCATs);
206 102 : stickyErr = stickyErr == CHIP_NO_ERROR ? err : stickyErr;
207 102 : if (err != CHIP_NO_ERROR)
208 : {
209 0 : ChipLogError(SecureChannel,
210 : "Session resumption cache deletion partially failed for fabric index %u, "
211 : "unable to load node state: %" CHIP_ERROR_FORMAT,
212 : fabricIndex, err.Format());
213 0 : continue;
214 : }
215 51 : err = DeleteLink(resumptionId);
216 102 : stickyErr = stickyErr == CHIP_NO_ERROR ? err : stickyErr;
217 102 : if (err != CHIP_NO_ERROR)
218 : {
219 0 : ChipLogError(SecureChannel,
220 : "Session resumption cache deletion partially failed for fabric index %u, "
221 : "unable to delete node link: %" CHIP_ERROR_FORMAT,
222 : fabricIndex, err.Format());
223 0 : continue;
224 : }
225 51 : err = DeleteState(index.mNodes[cur]);
226 102 : stickyErr = stickyErr == CHIP_NO_ERROR ? err : stickyErr;
227 102 : if (err != CHIP_NO_ERROR)
228 : {
229 0 : ChipLogError(SecureChannel,
230 : "Session resumption cache is in an inconsistent state! "
231 : "Unable to delete node state during attempted deletion of fabric index %u: %" CHIP_ERROR_FORMAT,
232 : fabricIndex, err.Format());
233 0 : continue;
234 : }
235 51 : ++found;
236 51 : --remain;
237 51 : if (remain)
238 : {
239 49 : memmove(&index.mNodes[cur], &index.mNodes[cur + 1], remain * sizeof(index.mNodes[0]));
240 : }
241 414 : }
242 19 : if (found)
243 : {
244 19 : index.mSize -= found;
245 19 : CHIP_ERROR err = SaveIndex(index);
246 38 : stickyErr = stickyErr == CHIP_NO_ERROR ? err : stickyErr;
247 38 : if (err != CHIP_NO_ERROR)
248 : {
249 0 : ChipLogError(
250 : SecureChannel,
251 : "Session resumption cache is in an inconsistent state! "
252 : "Unable to save session resumption index during attempted deletion of fabric index %u: %" CHIP_ERROR_FORMAT,
253 : fabricIndex, err.Format());
254 : }
255 : }
256 19 : return stickyErr;
257 : }
258 :
259 : } // namespace chip
|