102 lines
4.3 KiB
C#
102 lines
4.3 KiB
C#
// Copyright (c) Microsoft Open Technologies, Inc. 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.Security.Cryptography;
|
|
|
|
namespace Microsoft.AspNet.Security.DataProtection
|
|
{
|
|
internal sealed class TimeLimitedDataProtector : ITimeLimitedDataProtector
|
|
{
|
|
internal const string PurposeString = "Microsoft.AspNet.Security.DataProtection.TimeLimitedDataProtector";
|
|
|
|
public TimeLimitedDataProtector(IDataProtector innerProtector)
|
|
{
|
|
InnerProtector = innerProtector;
|
|
}
|
|
|
|
internal IDataProtector InnerProtector
|
|
{
|
|
get;
|
|
private set;
|
|
}
|
|
|
|
public ITimeLimitedDataProtector CreateProtector([NotNull] string purpose)
|
|
{
|
|
return new TimeLimitedDataProtector(InnerProtector.CreateProtector(purpose));
|
|
}
|
|
|
|
public byte[] Protect([NotNull] byte[] unprotectedData)
|
|
{
|
|
return Protect(unprotectedData, DateTimeOffset.MaxValue);
|
|
}
|
|
|
|
public byte[] Protect([NotNull] byte[] unprotectedData, DateTimeOffset expiration)
|
|
{
|
|
// We prepend the expiration time (as a big-endian 64-bit UTC tick count) to the unprotected data.
|
|
ulong utcTicksExpiration = (ulong)expiration.UtcTicks;
|
|
|
|
byte[] unprotectedDataWithHeader = new byte[checked(8 + unprotectedData.Length)];
|
|
unprotectedDataWithHeader[0] = (byte)(utcTicksExpiration >> 56);
|
|
unprotectedDataWithHeader[1] = (byte)(utcTicksExpiration >> 48);
|
|
unprotectedDataWithHeader[2] = (byte)(utcTicksExpiration >> 40);
|
|
unprotectedDataWithHeader[3] = (byte)(utcTicksExpiration >> 32);
|
|
unprotectedDataWithHeader[4] = (byte)(utcTicksExpiration >> 24);
|
|
unprotectedDataWithHeader[5] = (byte)(utcTicksExpiration >> 16);
|
|
unprotectedDataWithHeader[6] = (byte)(utcTicksExpiration >> 8);
|
|
unprotectedDataWithHeader[7] = (byte)(utcTicksExpiration);
|
|
Buffer.BlockCopy(unprotectedData, 0, unprotectedDataWithHeader, 8, unprotectedData.Length);
|
|
|
|
return InnerProtector.Protect(unprotectedDataWithHeader);
|
|
}
|
|
|
|
public byte[] Unprotect([NotNull] byte[] protectedData)
|
|
{
|
|
DateTimeOffset unused;
|
|
return Unprotect(protectedData, out unused);
|
|
}
|
|
|
|
public byte[] Unprotect([NotNull] byte[] protectedData, out DateTimeOffset expiration)
|
|
{
|
|
try
|
|
{
|
|
byte[] unprotectedDataWithHeader = InnerProtector.Unprotect(protectedData);
|
|
CryptoUtil.Assert(unprotectedDataWithHeader.Length >= 8, "No header present.");
|
|
|
|
// Read expiration time back out of the payload
|
|
ulong utcTicksExpiration = (((ulong)unprotectedDataWithHeader[0]) << 56)
|
|
| (((ulong)unprotectedDataWithHeader[1]) << 48)
|
|
| (((ulong)unprotectedDataWithHeader[2]) << 40)
|
|
| (((ulong)unprotectedDataWithHeader[3]) << 32)
|
|
| (((ulong)unprotectedDataWithHeader[4]) << 24)
|
|
| (((ulong)unprotectedDataWithHeader[5]) << 16)
|
|
| (((ulong)unprotectedDataWithHeader[6]) << 8)
|
|
| (ulong)unprotectedDataWithHeader[7];
|
|
|
|
// Are we expired?
|
|
DateTime utcNow = DateTime.UtcNow;
|
|
if ((ulong)utcNow.Ticks > utcTicksExpiration)
|
|
{
|
|
throw Error.TimeLimitedDataProtector_PayloadExpired(utcTicksExpiration);
|
|
}
|
|
|
|
byte[] retVal = new byte[unprotectedDataWithHeader.Length - 8];
|
|
Buffer.BlockCopy(unprotectedDataWithHeader, 8, retVal, 0, retVal.Length);
|
|
|
|
expiration = new DateTimeOffset((long)utcTicksExpiration, TimeSpan.Zero);
|
|
return retVal;
|
|
}
|
|
catch (Exception ex) if (!(ex is CryptographicException))
|
|
{
|
|
// Homogenize all failures to CryptographicException
|
|
throw Error.CryptCommon_GenericError(ex);
|
|
}
|
|
}
|
|
|
|
IDataProtector IDataProtectionProvider.CreateProtector([NotNull] string purpose)
|
|
{
|
|
return CreateProtector(purpose);
|
|
}
|
|
}
|
|
}
|