aspnetcore/src/Microsoft.AspNet.DataProtec.../TimeLimitedDataProtector.cs

103 lines
4.1 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 Microsoft.AspNet.Cryptography;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.DataProtection
{
internal sealed class TimeLimitedDataProtector : ITimeLimitedDataProtector
{
internal const string PurposeString = "Microsoft.AspNet.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[] plaintext)
{
return Protect(plaintext, DateTimeOffset.MaxValue);
}
public byte[] Protect([NotNull] byte[] plaintext, 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[] plaintextWithHeader = new byte[checked(8 + plaintext.Length)];
plaintextWithHeader[0] = (byte)(utcTicksExpiration >> 56);
plaintextWithHeader[1] = (byte)(utcTicksExpiration >> 48);
plaintextWithHeader[2] = (byte)(utcTicksExpiration >> 40);
plaintextWithHeader[3] = (byte)(utcTicksExpiration >> 32);
plaintextWithHeader[4] = (byte)(utcTicksExpiration >> 24);
plaintextWithHeader[5] = (byte)(utcTicksExpiration >> 16);
plaintextWithHeader[6] = (byte)(utcTicksExpiration >> 8);
plaintextWithHeader[7] = (byte)(utcTicksExpiration);
Buffer.BlockCopy(plaintext, 0, plaintextWithHeader, 8, plaintext.Length);
return InnerProtector.Protect(plaintextWithHeader);
}
public byte[] Unprotect([NotNull] byte[] protectedData)
{
DateTimeOffset unused;
return Unprotect(protectedData, out unused);
}
public byte[] Unprotect([NotNull] byte[] protectedData, out DateTimeOffset expiration)
{
try
{
byte[] plaintextWithHeader = InnerProtector.Unprotect(protectedData);
CryptoUtil.Assert(plaintextWithHeader.Length >= 8, "No header present.");
// Read expiration time back out of the payload
ulong utcTicksExpiration = (((ulong)plaintextWithHeader[0]) << 56)
| (((ulong)plaintextWithHeader[1]) << 48)
| (((ulong)plaintextWithHeader[2]) << 40)
| (((ulong)plaintextWithHeader[3]) << 32)
| (((ulong)plaintextWithHeader[4]) << 24)
| (((ulong)plaintextWithHeader[5]) << 16)
| (((ulong)plaintextWithHeader[6]) << 8)
| (ulong)plaintextWithHeader[7];
// Are we expired?
DateTime utcNow = DateTime.UtcNow;
if ((ulong)utcNow.Ticks > utcTicksExpiration)
{
throw Error.TimeLimitedDataProtector_PayloadExpired(utcTicksExpiration);
}
byte[] retVal = new byte[plaintextWithHeader.Length - 8];
Buffer.BlockCopy(plaintextWithHeader, 8, retVal, 0, retVal.Length);
expiration = new DateTimeOffset((long)utcTicksExpiration, TimeSpan.Zero);
return retVal;
}
catch (Exception ex) when (ex.RequiresHomogenization())
{
// Homogenize all failures to CryptographicException
throw Error.CryptCommon_GenericError(ex);
}
}
IDataProtector IDataProtectionProvider.CreateProtector([NotNull] string purpose)
{
return CreateProtector(purpose);
}
}
}