@@ -147,26 +147,20 @@ private bool CanCreateAuthenticatedEncryptor(IKey key, ref int retriesRemaining)
147147
148148 private IKey ? FindDefaultKey ( DateTimeOffset now , IEnumerable < IKey > allKeys , out IKey ? fallbackKey )
149149 {
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
165162 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 ( ) ;
170164
171165 var decryptRetriesRemaining = _maxDecryptRetries ;
172166
@@ -192,18 +186,19 @@ where key.CreationDate > propagationCutoff
192186 // key has propagated to all callers (so its creation date should be before the previous
193187 // propagation period), and we cannot use revoked keys. The fallback key may be expired.
194188
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.
196191
197192 // Unlike for the preferred key, we don't choose a fallback key and then reject it if
198193 // CanCreateAuthenticatedEncryptor is false. We want to end up with *some* key, so we
199194 // keep trying until we find one that works.
200195 var unrevokedKeys = allKeys . Where ( key => ! key . IsRevoked ) ;
201196 fallbackKey = ( from key in ( from key in unrevokedKeys
202197 where ! ReferenceEquals ( key , preferredDefaultKey ) // Don't reconsider it as a fallback
203- where key . CreationDate <= propagationCutoff
198+ where key . CreationDate <= now - _keyPropagationWindow
204199 orderby key . CreationDate descending
205200 select key ) . Concat ( from key in unrevokedKeys
206- where key . CreationDate > propagationCutoff
201+ where key . CreationDate > now - _keyPropagationWindow
207202 orderby key . CreationDate ascending
208203 select key )
209204 where CanCreateAuthenticatedEncryptor ( key , ref decryptRetriesRemaining )
0 commit comments