| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179 |
- // Copyright (c) .NET Foundation. All rights reserved.
- // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
- using System;
- using System.Globalization;
- using System.Security.Cryptography;
- using Microsoft.AspNetCore.DataProtection.Extensions;
- using Microsoft.Extensions.Logging.Abstractions;
- using Moq;
- using Xunit;
- namespace Microsoft.AspNetCore.DataProtection
- {
- public class TimeLimitedDataProtectorTests
- {
- private const string TimeLimitedPurposeString = "Microsoft.AspNetCore.DataProtection.TimeLimitedDataProtector.v1";
- [Fact]
- public void Protect_LifetimeSpecified()
- {
- // Arrange
- // 0x08c1220247e44000 is the representation of midnight 2000-01-01 UTC.
- DateTimeOffset expiration = StringToDateTime("2000-01-01 00:00:00Z");
- var mockInnerProtector = new Mock<IDataProtector>();
- mockInnerProtector.Setup(o => o.CreateProtector("new purpose").CreateProtector(TimeLimitedPurposeString).Protect(
- new byte[] {
- 0x08, 0xc1, 0x22, 0x02, 0x47, 0xe4, 0x40, 0x00, /* header */
- 0x01, 0x02, 0x03, 0x04, 0x05 /* payload */
- })).Returns(new byte[] { 0x10, 0x11 });
- var timeLimitedProtector = new TimeLimitedDataProtector(mockInnerProtector.Object);
- // Act
- var subProtector = timeLimitedProtector.CreateProtector("new purpose");
- var protectedPayload = subProtector.Protect(new byte[] { 0x01, 0x02, 0x03, 0x04, 0x05 }, expiration);
- // Assert
- Assert.Equal(new byte[] { 0x10, 0x11 }, protectedPayload);
- }
- [Fact]
- public void Protect_LifetimeNotSpecified_UsesInfiniteLifetime()
- {
- // Arrange
- // 0x2bca2875f4373fff is the representation of DateTimeOffset.MaxValue.
- DateTimeOffset expiration = StringToDateTime("2000-01-01 00:00:00Z");
- var mockInnerProtector = new Mock<IDataProtector>();
- mockInnerProtector.Setup(o => o.CreateProtector("new purpose").CreateProtector(TimeLimitedPurposeString).Protect(
- new byte[] {
- 0x2b, 0xca, 0x28, 0x75, 0xf4, 0x37, 0x3f, 0xff, /* header */
- 0x01, 0x02, 0x03, 0x04, 0x05 /* payload */
- })).Returns(new byte[] { 0x10, 0x11 });
- var timeLimitedProtector = new TimeLimitedDataProtector(mockInnerProtector.Object);
- // Act
- var subProtector = timeLimitedProtector.CreateProtector("new purpose");
- var protectedPayload = subProtector.Protect(new byte[] { 0x01, 0x02, 0x03, 0x04, 0x05 });
- // Assert
- Assert.Equal(new byte[] { 0x10, 0x11 }, protectedPayload);
- }
- [Fact]
- public void Unprotect_WithinPayloadValidityPeriod_Success()
- {
- // Arrange
- // 0x08c1220247e44000 is the representation of midnight 2000-01-01 UTC.
- DateTimeOffset expectedExpiration = StringToDateTime("2000-01-01 00:00:00Z");
- DateTimeOffset now = StringToDateTime("1999-01-01 00:00:00Z");
- var mockInnerProtector = new Mock<IDataProtector>();
- mockInnerProtector.Setup(o => o.CreateProtector(TimeLimitedPurposeString).Unprotect(new byte[] { 0x10, 0x11 })).Returns(
- new byte[] {
- 0x08, 0xc1, 0x22, 0x02, 0x47, 0xe4, 0x40, 0x00, /* header */
- 0x01, 0x02, 0x03, 0x04, 0x05 /* payload */
- });
- var timeLimitedProtector = new TimeLimitedDataProtector(mockInnerProtector.Object);
- // Act
- DateTimeOffset actualExpiration;
- var retVal = timeLimitedProtector.UnprotectCore(new byte[] { 0x10, 0x11 }, now, out actualExpiration);
- // Assert
- Assert.Equal(expectedExpiration, actualExpiration);
- Assert.Equal(new byte[] { 0x01, 0x02, 0x03, 0x04, 0x05 }, retVal);
- }
- [Fact]
- public void Unprotect_PayloadHasExpired_Fails()
- {
- // Arrange
- // 0x08c1220247e44000 is the representation of midnight 2000-01-01 UTC.
- DateTimeOffset expectedExpiration = StringToDateTime("2000-01-01 00:00:00Z");
- DateTimeOffset now = StringToDateTime("2001-01-01 00:00:00Z");
- var mockInnerProtector = new Mock<IDataProtector>();
- mockInnerProtector.Setup(o => o.CreateProtector(TimeLimitedPurposeString).Unprotect(new byte[] { 0x10, 0x11 })).Returns(
- new byte[] {
- 0x08, 0xc1, 0x22, 0x02, 0x47, 0xe4, 0x40, 0x00, /* header */
- 0x01, 0x02, 0x03, 0x04, 0x05 /* payload */
- });
- var timeLimitedProtector = new TimeLimitedDataProtector(mockInnerProtector.Object);
- // Act & assert
- DateTimeOffset unused;
- var ex = Assert.Throws<CryptographicException>(() => timeLimitedProtector.UnprotectCore(new byte[] { 0x10, 0x11 }, now, out unused));
- // Assert
- Assert.Equal(Resources.FormatTimeLimitedDataProtector_PayloadExpired(expectedExpiration), ex.Message);
- }
- [Fact]
- public void Unprotect_ProtectedDataMalformed_Fails()
- {
- // Arrange
- // 0x08c1220247e44000 is the representation of midnight 2000-01-01 UTC.
- var mockInnerProtector = new Mock<IDataProtector>();
- mockInnerProtector.Setup(o => o.CreateProtector(TimeLimitedPurposeString).Unprotect(new byte[] { 0x10, 0x11 })).Returns(
- new byte[] {
- 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 /* header too short */
- });
- var timeLimitedProtector = new TimeLimitedDataProtector(mockInnerProtector.Object);
- // Act & assert
- DateTimeOffset unused;
- var ex = Assert.Throws<CryptographicException>(() => timeLimitedProtector.Unprotect(new byte[] { 0x10, 0x11 }, out unused));
- // Assert
- Assert.Equal(Resources.TimeLimitedDataProtector_PayloadInvalid, ex.Message);
- }
- [Fact]
- public void Unprotect_UnprotectOperationFails_HomogenizesExceptionToCryptographicException()
- {
- // Arrange
- // 0x08c1220247e44000 is the representation of midnight 2000-01-01 UTC.
- var mockInnerProtector = new Mock<IDataProtector>();
- mockInnerProtector.Setup(o => o.CreateProtector(TimeLimitedPurposeString).Unprotect(new byte[] { 0x10, 0x11 })).Throws(new Exception("How exceptional!"));
- var timeLimitedProtector = new TimeLimitedDataProtector(mockInnerProtector.Object);
- // Act & assert
- DateTimeOffset unused;
- var ex = Assert.Throws<CryptographicException>(() => timeLimitedProtector.Unprotect(new byte[] { 0x10, 0x11 }, out unused));
- // Assert
- Assert.Equal(Resources.CryptCommon_GenericError, ex.Message);
- Assert.Equal("How exceptional!", ex.InnerException.Message);
- }
- [Fact]
- public void RoundTrip_ProtectedData()
- {
- // Arrange
- var ephemeralProtector = new EphemeralDataProtectionProvider(NullLoggerFactory.Instance).CreateProtector("my purpose");
- var timeLimitedProtector = new TimeLimitedDataProtector(ephemeralProtector);
- var expectedExpiration = StringToDateTime("2020-01-01 00:00:00Z");
- // Act
- byte[] ephemeralProtectedPayload = ephemeralProtector.Protect(new byte[] { 0x01, 0x02, 0x03, 0x04 });
- byte[] timeLimitedProtectedPayload = timeLimitedProtector.Protect(new byte[] { 0x11, 0x22, 0x33, 0x44 }, expectedExpiration);
- // Assert
- DateTimeOffset actualExpiration;
- Assert.Equal(new byte[] { 0x11, 0x22, 0x33, 0x44 }, timeLimitedProtector.UnprotectCore(timeLimitedProtectedPayload, StringToDateTime("2010-01-01 00:00:00Z"), out actualExpiration));
- Assert.Equal(expectedExpiration, actualExpiration);
- // the two providers shouldn't be able to talk to one another (due to the purpose chaining)
- Assert.Throws<CryptographicException>(() => ephemeralProtector.Unprotect(timeLimitedProtectedPayload));
- Assert.Throws<CryptographicException>(() => timeLimitedProtector.Unprotect(ephemeralProtectedPayload, out actualExpiration));
- }
- private static DateTime StringToDateTime(string input)
- {
- return DateTimeOffset.ParseExact(input, "u", CultureInfo.InvariantCulture).UtcDateTime;
- }
- }
- }
|