@@ -147,26 +147,20 @@ private bool CanCreateAuthenticatedEncryptor(IKey key, ref int retriesRemaining)
147
147
148
148
private IKey ? FindDefaultKey ( DateTimeOffset now , IEnumerable < IKey > allKeys , out IKey ? fallbackKey )
149
149
{
150
- // Keys created before this time should have propagated to all instances.
151
- var propagationCutoff = now - _keyPropagationWindow ;
152
-
153
- // Prefer the most recently activated key that's old enough to have propagated to all instances.
154
- // If no such key exists, fall back to the *least* recently activated key that's too new to have
155
- // propagated to all instances.
156
-
157
- // An unpropagated key can still be preferred insofar as we wouldn't want to generate a replacement
158
- // for it (as the replacement would also be unpropagated).
159
-
160
- // Note that the two sort orders are opposite: we want the *newest* key that's old enough
161
- // (to have been propagated) or the *oldest* key that's too new.
162
- var activatedKeys = allKeys . Where ( key => key . ActivationDate <= now + _maxServerToServerClockSkew ) ;
163
- var preferredDefaultKey = ( from key in activatedKeys
164
- where key . CreationDate <= propagationCutoff
150
+ // find the preferred default key (allowing for server-to-server clock skew)
151
+
152
+ // Note: another approach would be to prefer the *least-recently* activated key if none
153
+ // are old enough to have been propagated. We tried that and reverted it because it
154
+ // made us less resilient against bad keys. This way, we'll reject a bad key and generate
155
+ // a replacement and then the next instance to run will pick up the replacement, rather
156
+ // than the bad key. It did have the conceptual advantage of being more similar to the
157
+ // fallback code below and the hypothetical advantage of making it easier for instances
158
+ // to choose the same key in the event of a race (though we never managed to show that
159
+ // empirically. See also https://github.com/dotnet/aspnetcore/issues/57137.
160
+ var preferredDefaultKey = ( from key in allKeys
161
+ where key . ActivationDate <= now + _maxServerToServerClockSkew
165
162
orderby key . ActivationDate descending, key . KeyId ascending
166
- select key ) . Concat ( from key in activatedKeys
167
- where key . CreationDate > propagationCutoff
168
- orderby key . ActivationDate ascending, key . KeyId ascending
169
- select key ) . FirstOrDefault ( ) ;
163
+ select key ) . FirstOrDefault ( ) ;
170
164
171
165
var decryptRetriesRemaining = _maxDecryptRetries ;
172
166
@@ -192,18 +186,19 @@ where key.CreationDate > propagationCutoff
192
186
// key has propagated to all callers (so its creation date should be before the previous
193
187
// propagation period), and we cannot use revoked keys. The fallback key may be expired.
194
188
195
- // As above, the two sort orders are opposite.
189
+ // Note that the two sort orders are opposite: we want the *newest* key that's old enough
190
+ // (to have been propagated) or the *oldest* key that's too new.
196
191
197
192
// Unlike for the preferred key, we don't choose a fallback key and then reject it if
198
193
// CanCreateAuthenticatedEncryptor is false. We want to end up with *some* key, so we
199
194
// keep trying until we find one that works.
200
195
var unrevokedKeys = allKeys . Where ( key => ! key . IsRevoked ) ;
201
196
fallbackKey = ( from key in ( from key in unrevokedKeys
202
197
where ! ReferenceEquals ( key , preferredDefaultKey ) // Don't reconsider it as a fallback
203
- where key . CreationDate <= propagationCutoff
198
+ where key . CreationDate <= now - _keyPropagationWindow
204
199
orderby key . CreationDate descending
205
200
select key ) . Concat ( from key in unrevokedKeys
206
- where key . CreationDate > propagationCutoff
201
+ where key . CreationDate > now - _keyPropagationWindow
207
202
orderby key . CreationDate ascending
208
203
select key )
209
204
where CanCreateAuthenticatedEncryptor ( key , ref decryptRetriesRemaining )
0 commit comments