Ejemplo n.º 1
0
    public void Unprotect_KeyNotFound_RefreshOnce_ThrowsKeyNotFound()
    {
        // Arrange
        Guid notFoundKeyId = new Guid("654057ab-2491-4471-a72a-b3b114afda38");

        byte[] protectedData = BuildProtectedDataFromCiphertext(
            keyId: notFoundKeyId,
            ciphertext: new byte[0]);

        var mockDescriptor       = new Mock <IAuthenticatedEncryptorDescriptor>();
        var mockEncryptorFactory = new Mock <IAuthenticatedEncryptorFactory>();

        mockEncryptorFactory.Setup(o => o.CreateEncryptorInstance(It.IsAny <IKey>())).Returns(new Mock <IAuthenticatedEncryptor>().Object);
        var encryptorFactory = new AuthenticatedEncryptorFactory(NullLoggerFactory.Instance);

        // the keyring has only one key
        Key key     = new Key(Guid.Empty, DateTimeOffset.Now, DateTimeOffset.Now, DateTimeOffset.Now, mockDescriptor.Object, new[] { mockEncryptorFactory.Object });
        var keyRing = new CacheableKeyRing(CancellationToken.None, DateTimeOffset.MaxValue, key, new[] { key });

        var keyRingProvider = CreateKeyRingProvider(new TestKeyRingProvider(keyRing));

        IDataProtector protector = new KeyRingBasedDataProtector(
            keyRingProvider: keyRingProvider,
            logger: GetLogger(),
            originalPurposes: null,
            newPurpose: "purpose");

        // Act & assert
        var ex = ExceptionAssert2.ThrowsCryptographicException(() => protector.Unprotect(protectedData));

        Assert.Equal(Error.Common_KeyNotFound(notFoundKeyId).Message, ex.Message);
    }
Ejemplo n.º 2
0
    public void KeyRing_Prop()
    {
        // Arrange
        var keyRing          = new Mock <IKeyRing>().Object;
        var cacheableKeyRing = new CacheableKeyRing(CancellationToken.None, DateTimeOffset.Now, keyRing);

        // Act & assert
        Assert.Same(keyRing, cacheableKeyRing.KeyRing);
    }
        public void KeyRing_Prop()
        {
            // Arrange
            var keyRing = new Mock<IKeyRing>().Object;
            var cacheableKeyRing = new CacheableKeyRing(CancellationToken.None, DateTimeOffset.Now, keyRing);

            // Act & assert
            Assert.Same(keyRing, cacheableKeyRing.KeyRing);
        }
Ejemplo n.º 4
0
    public void IsValid_Expired_ReturnsFalse()
    {
        // Arrange
        var            keyRing          = new Mock <IKeyRing>().Object;
        DateTimeOffset now              = DateTimeOffset.UtcNow;
        var            cts              = new CancellationTokenSource();
        var            cacheableKeyRing = new CacheableKeyRing(cts.Token, now.AddHours(1), keyRing);

        // Act & assert
        Assert.True(CacheableKeyRing.IsValid(cacheableKeyRing, now.UtcDateTime));
        Assert.False(CacheableKeyRing.IsValid(cacheableKeyRing, now.AddHours(1).UtcDateTime));
    }
        public void IsValid_Expired_ReturnsFalse()
        {
            // Arrange
            var keyRing = new Mock<IKeyRing>().Object;
            DateTimeOffset now = DateTimeOffset.UtcNow;
            var cts = new CancellationTokenSource();
            var cacheableKeyRing = new CacheableKeyRing(cts.Token, now.AddHours(1), keyRing);

            // Act & assert
            Assert.True(CacheableKeyRing.IsValid(cacheableKeyRing, now.UtcDateTime));
            Assert.False(CacheableKeyRing.IsValid(cacheableKeyRing, now.AddHours(1).UtcDateTime));
        }
Ejemplo n.º 6
0
        public void CreateCacheableKeyRing_GenerationRequired_NoDefaultKey_CreatesNewKeyWithImmediateActivation()
        {
            // Arrange
            var callSequence   = new List <string>();
            var expirationCts1 = new CancellationTokenSource();
            var expirationCts2 = new CancellationTokenSource();

            var now      = StringToDateTime("2015-03-01 00:00:00Z");
            var allKeys1 = new IKey[0];

            var key1     = CreateKey("2015-03-01 00:00:00Z", "2016-03-01 00:00:00Z");
            var key2     = CreateKey("2016-03-01 00:00:00Z", "2017-03-01 00:00:00Z");
            var allKeys2 = new[] { key1, key2 };

            var keyRingProvider = SetupCreateCacheableKeyRingTestAndCreateKeyManager(
                callSequence: callSequence,
                getCacheExpirationTokenReturnValues: new[] { expirationCts1.Token, expirationCts2.Token },
                getAllKeysReturnValues: new[] { allKeys1, allKeys2 },
                createNewKeyCallbacks: new[] {
                Tuple.Create((DateTimeOffset)now, (DateTimeOffset)now + TimeSpan.FromDays(90), CreateKey())
            },
                resolveDefaultKeyPolicyReturnValues: new[]
            {
                Tuple.Create((DateTimeOffset)now, (IEnumerable <IKey>)allKeys1, new DefaultKeyResolution()
                {
                    DefaultKey           = null,
                    ShouldGenerateNewKey = true
                }),
                Tuple.Create((DateTimeOffset)now, (IEnumerable <IKey>)allKeys2, new DefaultKeyResolution()
                {
                    DefaultKey           = key1,
                    ShouldGenerateNewKey = false
                })
            });

            // Act
            var cacheableKeyRing = keyRingProvider.GetCacheableKeyRing(now);

            // Assert
            Assert.Equal(key1.KeyId, cacheableKeyRing.KeyRing.DefaultKeyId);
            AssertWithinJitterRange(cacheableKeyRing.ExpirationTimeUtc, now);
            Assert.True(CacheableKeyRing.IsValid(cacheableKeyRing, now));
            expirationCts1.Cancel();
            Assert.True(CacheableKeyRing.IsValid(cacheableKeyRing, now));
            expirationCts2.Cancel();
            Assert.False(CacheableKeyRing.IsValid(cacheableKeyRing, now));
            Assert.Equal(new[] { "GetCacheExpirationToken", "GetAllKeys", "ResolveDefaultKeyPolicy", "CreateNewKey", "GetCacheExpirationToken", "GetAllKeys", "ResolveDefaultKeyPolicy" }, callSequence);
        }
Ejemplo n.º 7
0
        public void CreateCacheableKeyRing_GenerationRequired_WithFallbackKey_KeyGenerationDisabled_DoesNotCreateDefaultKey()
        {
            // Arrange
            var callSequence  = new List <string>();
            var expirationCts = new CancellationTokenSource();

            var now     = StringToDateTime("2016-02-01 00:00:00Z");
            var key1    = CreateKey("2015-03-01 00:00:00Z", "2015-03-01 00:00:00Z");
            var allKeys = new[] { key1 };

            var keyRingProvider = SetupCreateCacheableKeyRingTestAndCreateKeyManager(
                callSequence: callSequence,
                getCacheExpirationTokenReturnValues: new[] { expirationCts.Token },
                getAllKeysReturnValues: new[] { allKeys },
                createNewKeyCallbacks: null, // empty
                resolveDefaultKeyPolicyReturnValues: new[]
            {
                Tuple.Create((DateTimeOffset)now, (IEnumerable <IKey>)allKeys, new DefaultKeyResolution()
                {
                    FallbackKey          = key1,
                    ShouldGenerateNewKey = true
                })
            },
                keyManagementOptions: new KeyManagementOptions()
            {
                AutoGenerateKeys = false
            });

            // Act
            var cacheableKeyRing = keyRingProvider.GetCacheableKeyRing(now);

            // Assert
            Assert.Equal(key1.KeyId, cacheableKeyRing.KeyRing.DefaultKeyId);
            AssertWithinJitterRange(cacheableKeyRing.ExpirationTimeUtc, now);
            Assert.True(CacheableKeyRing.IsValid(cacheableKeyRing, now));
            expirationCts.Cancel();
            Assert.False(CacheableKeyRing.IsValid(cacheableKeyRing, now));
            Assert.Equal(new[] { "GetCacheExpirationToken", "GetAllKeys", "ResolveDefaultKeyPolicy" }, callSequence);
        }
Ejemplo n.º 8
0
        public void CreateCacheableKeyRing_NoGenerationRequired_DefaultKeyExpiresBeforeRefreshPeriod()
        {
            // Arrange
            var callSequence  = new List <string>();
            var expirationCts = new CancellationTokenSource();

            var now     = StringToDateTime("2016-02-29 20:00:00Z");
            var key1    = CreateKey("2015-03-01 00:00:00Z", "2016-03-01 00:00:00Z");
            var key2    = CreateKey("2016-03-01 00:00:00Z", "2017-03-01 00:00:00Z");
            var allKeys = new[] { key1, key2 };

            var keyRingProvider = SetupCreateCacheableKeyRingTestAndCreateKeyManager(
                callSequence: callSequence,
                getCacheExpirationTokenReturnValues: new[] { expirationCts.Token },
                getAllKeysReturnValues: new[] { allKeys },
                createNewKeyCallbacks: null,
                resolveDefaultKeyPolicyReturnValues: new[]
            {
                Tuple.Create((DateTimeOffset)now, (IEnumerable <IKey>)allKeys, new DefaultKeyResolution()
                {
                    DefaultKey           = key1,
                    ShouldGenerateNewKey = false
                })
            });

            // Act
            var cacheableKeyRing = keyRingProvider.GetCacheableKeyRing(now);

            // Assert
            Assert.Equal(key1.KeyId, cacheableKeyRing.KeyRing.DefaultKeyId);
            Assert.Equal(StringToDateTime("2016-03-01 00:00:00Z"), cacheableKeyRing.ExpirationTimeUtc);
            Assert.True(CacheableKeyRing.IsValid(cacheableKeyRing, now));
            expirationCts.Cancel();
            Assert.False(CacheableKeyRing.IsValid(cacheableKeyRing, now));
            Assert.Equal(new[] { "GetCacheExpirationToken", "GetAllKeys", "ResolveDefaultKeyPolicy" }, callSequence);
        }
Ejemplo n.º 9
0
        internal IKeyRing GetCurrentKeyRingCore(DateTime utcNow, bool forceRefresh = false)
        {
            Debug.Assert(utcNow.Kind == DateTimeKind.Utc);

            // Can we return the cached keyring to the caller?
            CacheableKeyRing?existingCacheableKeyRing = null;

            if (!forceRefresh)
            {
                existingCacheableKeyRing = Volatile.Read(ref _cacheableKeyRing);
                if (CacheableKeyRing.IsValid(existingCacheableKeyRing, utcNow))
                {
                    return(existingCacheableKeyRing.KeyRing);
                }
            }

            // The cached keyring hasn't been created or must be refreshed. We'll allow one thread to
            // update the keyring, and all other threads will continue to use the existing cached
            // keyring while the first thread performs the update. There is an exception: if there
            // is no usable existing cached keyring, all callers must block until the keyring exists.
            var acquiredLock = false;

            try
            {
                Monitor.TryEnter(_cacheableKeyRingLockObj, (existingCacheableKeyRing != null) ? 0 : Timeout.Infinite, ref acquiredLock);
                if (acquiredLock)
                {
                    if (!forceRefresh)
                    {
                        // This thread acquired the critical section and is responsible for updating the
                        // cached keyring. But first, let's make sure that somebody didn't sneak in before
                        // us and update the keyring on our behalf.
                        existingCacheableKeyRing = Volatile.Read(ref _cacheableKeyRing);
                        if (CacheableKeyRing.IsValid(existingCacheableKeyRing, utcNow))
                        {
                            return(existingCacheableKeyRing.KeyRing);
                        }

                        if (existingCacheableKeyRing != null)
                        {
                            _logger.ExistingCachedKeyRingIsExpired();
                        }
                    }

                    // It's up to us to refresh the cached keyring.
                    // This call is performed *under lock*.
                    CacheableKeyRing newCacheableKeyRing;

                    try
                    {
                        newCacheableKeyRing = CacheableKeyRingProvider.GetCacheableKeyRing(utcNow);
                    }
                    catch (Exception ex)
                    {
                        if (existingCacheableKeyRing != null)
                        {
                            _logger.ErrorOccurredWhileRefreshingKeyRing(ex);
                        }
                        else
                        {
                            _logger.ErrorOccurredWhileReadingKeyRing(ex);
                        }

                        // Failures that occur while refreshing the keyring are most likely transient, perhaps due to a
                        // temporary network outage. Since we don't want every subsequent call to result in failure, we'll
                        // create a new keyring object whose expiration is now + some short period of time (currently 2 min),
                        // and after this period has elapsed the next caller will try refreshing. If we don't have an
                        // existing keyring (perhaps because this is the first call), then there's nothing to extend, so
                        // each subsequent caller will keep going down this code path until one succeeds.
                        if (existingCacheableKeyRing != null)
                        {
                            Volatile.Write(ref _cacheableKeyRing, existingCacheableKeyRing.WithTemporaryExtendedLifetime(utcNow));
                        }

                        // The immediate caller should fail so that they can report the error up the chain. This makes it more likely
                        // that an administrator can see the error and react to it as appropriate. The caller can retry the operation
                        // and will probably have success as long as they fall within the temporary extension mentioned above.
                        throw;
                    }

                    Volatile.Write(ref _cacheableKeyRing, newCacheableKeyRing);
                    return(newCacheableKeyRing.KeyRing);
                }
                else
                {
                    // We didn't acquire the critical section. This should only occur if we passed
                    // zero for the Monitor.TryEnter timeout, which implies that we had an existing
                    // (but outdated) keyring that we can use as a fallback.
                    Debug.Assert(existingCacheableKeyRing != null);
                    return(existingCacheableKeyRing.KeyRing);
                }
            }
            finally
            {
                if (acquiredLock)
                {
                    Monitor.Exit(_cacheableKeyRingLockObj);
                }
            }
        }
Ejemplo n.º 10
0
 public RefreshTestKeyRingProvider(CacheableKeyRing keys, CacheableKeyRing refreshKeys)
 {
     _keyRing        = keys;
     _refreshKeyRing = refreshKeys;
 }
Ejemplo n.º 11
0
 public TestKeyRingProvider(CacheableKeyRing keys) => _keyRing = keys;
Ejemplo n.º 12
0
 public void IsValid_NullKeyRing_ReturnsFalse()
 {
     Assert.False(CacheableKeyRing.IsValid(null, DateTime.UtcNow));
 }