103 lines
4.1 KiB
C#
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);
|
|
}
|
|
}
|
|
}
|