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 58 : 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 58 : 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 148 : if (index.mSize == CHIP_CONFIG_CASE_SESSION_RESUME_CACHE_SIZE)
94 : {
95 : // TODO: implement LRU for resumption
96 1 : ReturnErrorOnFailure(Delete(index.mNodes[0]));
97 1 : ReturnErrorOnFailure(LoadIndex(index));
98 : }
99 :
100 148 : ReturnErrorOnFailure(SaveState(node, resumptionId, sharedSecret, peerCATs));
101 148 : ReturnErrorOnFailure(SaveLink(resumptionId, node));
102 :
103 148 : index.mNodes[index.mSize++] = node;
104 148 : ReturnErrorOnFailure(SaveIndex(index));
105 :
106 148 : return CHIP_NO_ERROR;
107 : }
108 :
109 49 : CHIP_ERROR DefaultSessionResumptionStorage::Delete(const ScopedNodeId & node)
110 : {
111 49 : SessionIndex index;
112 49 : ReturnErrorOnFailure(LoadIndex(index));
113 :
114 : ResumptionIdStorage resumptionId;
115 49 : Crypto::P256ECDHDerivedSecret sharedSecret;
116 49 : CATValues peerCATs;
117 49 : CHIP_ERROR err = LoadState(node, resumptionId, sharedSecret, peerCATs);
118 49 : if (err == CHIP_NO_ERROR)
119 : {
120 49 : err = DeleteLink(resumptionId);
121 49 : if (err != CHIP_NO_ERROR && err != CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND)
122 : {
123 0 : ChipLogError(SecureChannel,
124 : "Unable to delete session resumption link for node " ChipLogFormatX64 ": %" CHIP_ERROR_FORMAT,
125 : ChipLogValueX64(node.GetNodeId()), err.Format());
126 : }
127 : }
128 0 : else if (err != CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND)
129 : {
130 0 : ChipLogError(SecureChannel,
131 : "Unable to load session resumption state during session deletion for node " ChipLogFormatX64
132 : ": %" CHIP_ERROR_FORMAT,
133 : ChipLogValueX64(node.GetNodeId()), err.Format());
134 : }
135 :
136 49 : err = DeleteState(node);
137 49 : if (err != CHIP_NO_ERROR && err != CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND)
138 : {
139 0 : ChipLogError(SecureChannel, "Unable to delete session resumption state for node " ChipLogFormatX64 ": %" CHIP_ERROR_FORMAT,
140 : ChipLogValueX64(node.GetNodeId()), err.Format());
141 : }
142 :
143 49 : bool found = false;
144 1225 : for (size_t i = 0; i < index.mSize; ++i)
145 : {
146 1176 : if (found)
147 : {
148 : // index.mSize was decreased by 1 when found was set to true.
149 : // So the (i+1)th element isn't out of bounds.
150 1127 : index.mNodes[i] = index.mNodes[i + 1];
151 : }
152 : else
153 : {
154 49 : if (index.mNodes[i] == node)
155 : {
156 49 : found = true;
157 49 : if (i + 1 < index.mSize)
158 : {
159 48 : index.mNodes[i] = index.mNodes[i + 1];
160 : }
161 49 : index.mSize -= 1;
162 : }
163 : }
164 : }
165 :
166 49 : if (found)
167 : {
168 49 : err = SaveIndex(index);
169 49 : if (err != CHIP_NO_ERROR)
170 : {
171 0 : ChipLogError(SecureChannel, "Unable to save session resumption index: %" CHIP_ERROR_FORMAT, err.Format());
172 : }
173 : }
174 : else
175 : {
176 0 : ChipLogError(SecureChannel,
177 : "Unable to find session resumption state for node in index" ChipLogFormatX64 ": %" CHIP_ERROR_FORMAT,
178 : ChipLogValueX64(node.GetNodeId()), err.Format());
179 : }
180 :
181 49 : return CHIP_NO_ERROR;
182 49 : }
183 :
184 19 : CHIP_ERROR DefaultSessionResumptionStorage::DeleteAll(FabricIndex fabricIndex)
185 : {
186 19 : CHIP_ERROR stickyErr = CHIP_NO_ERROR;
187 19 : size_t found = 0;
188 19 : SessionIndex index;
189 19 : ReturnErrorOnFailure(LoadIndex(index));
190 19 : size_t initialSize = index.mSize;
191 433 : for (size_t i = 0; i < initialSize; ++i)
192 : {
193 414 : CHIP_ERROR err = CHIP_NO_ERROR;
194 414 : size_t cur = i - found;
195 414 : size_t remain = initialSize - i;
196 : ResumptionIdStorage resumptionId;
197 414 : Crypto::P256ECDHDerivedSecret sharedSecret;
198 414 : CATValues peerCATs;
199 414 : if (index.mNodes[cur].GetFabricIndex() != fabricIndex)
200 : {
201 363 : continue;
202 : }
203 51 : err = LoadState(index.mNodes[cur], resumptionId, sharedSecret, peerCATs);
204 51 : stickyErr = stickyErr == CHIP_NO_ERROR ? err : stickyErr;
205 51 : if (err != CHIP_NO_ERROR)
206 : {
207 0 : ChipLogError(SecureChannel,
208 : "Session resumption cache deletion partially failed for fabric index %u, "
209 : "unable to load node state: %" CHIP_ERROR_FORMAT,
210 : fabricIndex, err.Format());
211 0 : continue;
212 : }
213 51 : err = DeleteLink(resumptionId);
214 51 : stickyErr = stickyErr == CHIP_NO_ERROR ? err : stickyErr;
215 51 : if (err != CHIP_NO_ERROR)
216 : {
217 0 : ChipLogError(SecureChannel,
218 : "Session resumption cache deletion partially failed for fabric index %u, "
219 : "unable to delete node link: %" CHIP_ERROR_FORMAT,
220 : fabricIndex, err.Format());
221 0 : continue;
222 : }
223 51 : err = DeleteState(index.mNodes[cur]);
224 51 : stickyErr = stickyErr == CHIP_NO_ERROR ? err : stickyErr;
225 51 : if (err != CHIP_NO_ERROR)
226 : {
227 0 : ChipLogError(SecureChannel,
228 : "Session resumption cache is in an inconsistent state! "
229 : "Unable to delete node state during attempted deletion of fabric index %u: %" CHIP_ERROR_FORMAT,
230 : fabricIndex, err.Format());
231 0 : continue;
232 : }
233 51 : ++found;
234 51 : --remain;
235 51 : if (remain)
236 : {
237 49 : memmove(&index.mNodes[cur], &index.mNodes[cur + 1], remain * sizeof(index.mNodes[0]));
238 : }
239 414 : }
240 19 : if (found)
241 : {
242 19 : index.mSize -= found;
243 19 : CHIP_ERROR err = SaveIndex(index);
244 19 : stickyErr = stickyErr == CHIP_NO_ERROR ? err : stickyErr;
245 19 : if (err != CHIP_NO_ERROR)
246 : {
247 0 : ChipLogError(
248 : SecureChannel,
249 : "Session resumption cache is in an inconsistent state! "
250 : "Unable to save session resumption index during attempted deletion of fabric index %u: %" CHIP_ERROR_FORMAT,
251 : fabricIndex, err.Format());
252 : }
253 : }
254 19 : return stickyErr;
255 : }
256 :
257 : } // namespace chip
|