Simplify Guid reading/writing code in DataProtection (#36403)

Simplify Guid reading/writing code in DataProtection.
This commit is contained in:
Aditya Mandaleeka 2021-09-17 16:16:35 -07:00 коммит произвёл GitHub
Родитель bb0cde1935
Коммит 8392779338
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
1 изменённых файлов: 27 добавлений и 59 удалений

Просмотреть файл

@ -6,6 +6,7 @@ using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Runtime.CompilerServices;
using System.Threading; using System.Threading;
using Microsoft.AspNetCore.Cryptography; using Microsoft.AspNetCore.Cryptography;
using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption; using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption;
@ -128,7 +129,7 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
fixed (byte* pbRetVal = retVal) fixed (byte* pbRetVal = retVal)
{ {
WriteBigEndianInteger(pbRetVal, MAGIC_HEADER_V0); WriteBigEndianInteger(pbRetVal, MAGIC_HEADER_V0);
Write32bitAlignedGuid(&pbRetVal[sizeof(uint)], defaultKeyId); WriteGuid(&pbRetVal[sizeof(uint)], defaultKeyId);
} }
// At this point, retVal := { magicHeader || keyId || encryptorSpecificProtectedPayload } // At this point, retVal := { magicHeader || keyId || encryptorSpecificProtectedPayload }
@ -142,36 +143,17 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
} }
} }
// Helper function to read a GUID from a 32-bit alignment; useful on architectures where unaligned reads private static Guid ReadGuid(void* ptr)
// can result in weird behaviors at runtime.
private static Guid Read32bitAlignedGuid(void* ptr)
{ {
Debug.Assert((long)ptr % 4 == 0); #if NETCOREAPP
// Performs appropriate endianness fixups
Guid retVal; return new Guid(new ReadOnlySpan<byte>(ptr, sizeof(Guid)));
if (BitConverter.IsLittleEndian) #elif NETSTANDARD2_0 || NETFRAMEWORK
{ Debug.Assert(BitConverter.IsLittleEndian);
((int*)&retVal)[0] = ((int*)ptr)[0]; return Unsafe.ReadUnaligned<Guid>(ptr);
((int*)&retVal)[1] = ((int*)ptr)[1]; #else
((int*)&retVal)[2] = ((int*)ptr)[2]; #error Update target frameworks
((int*)&retVal)[3] = ((int*)ptr)[3]; #endif
}
else
{
// The first 8 bytes of the Guid hold one 32-bit integer
// and two 16-bit integers that must be byte-swapped.
((byte*)&retVal)[0] = ((byte*)ptr)[3];
((byte*)&retVal)[1] = ((byte*)ptr)[2];
((byte*)&retVal)[2] = ((byte*)ptr)[1];
((byte*)&retVal)[3] = ((byte*)ptr)[0];
((byte*)&retVal)[4] = ((byte*)ptr)[5];
((byte*)&retVal)[5] = ((byte*)ptr)[4];
((byte*)&retVal)[6] = ((byte*)ptr)[7];
((byte*)&retVal)[7] = ((byte*)ptr)[6];
((int*)&retVal)[2] = ((int*)ptr)[2];
((int*)&retVal)[3] = ((int*)ptr)[3];
}
return retVal;
} }
private static uint ReadBigEndian32BitInteger(byte* ptr) private static uint ReadBigEndian32BitInteger(byte* ptr)
@ -233,7 +215,7 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
fixed (byte* pbInput = protectedData) fixed (byte* pbInput = protectedData)
{ {
magicHeaderFromPayload = ReadBigEndian32BitInteger(pbInput); magicHeaderFromPayload = ReadBigEndian32BitInteger(pbInput);
keyIdFromPayload = Read32bitAlignedGuid(&pbInput[sizeof(uint)]); keyIdFromPayload = ReadGuid(&pbInput[sizeof(uint)]);
} }
// Are the magic header and version information correct? // Are the magic header and version information correct?
@ -318,34 +300,20 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
} }
} }
// Helper function to write a GUID to a 32-bit alignment; useful on ARM where unaligned reads private static void WriteGuid(void* ptr, Guid value)
// can result in weird behaviors at runtime.
private static void Write32bitAlignedGuid(void* ptr, Guid value)
{ {
Debug.Assert((long)ptr % 4 == 0); #if NETCOREAPP
var span = new Span<byte>(ptr, sizeof(Guid));
if (BitConverter.IsLittleEndian) // Performs appropriate endianness fixups
{ var success = value.TryWriteBytes(span);
((int*)ptr)[0] = ((int*)&value)[0]; Debug.Assert(success, "Failed to write Guid.");
((int*)ptr)[1] = ((int*)&value)[1]; #elif NETSTANDARD2_0 || NETFRAMEWORK
((int*)ptr)[2] = ((int*)&value)[2]; Debug.Assert(BitConverter.IsLittleEndian);
((int*)ptr)[3] = ((int*)&value)[3]; Unsafe.WriteUnaligned<Guid>(ptr, value);
} #else
else #error Update target frameworks
{ #endif
// The first 8 bytes of the Guid hold one 32-bit integer
// and two 16-bit integers that must be byte-swapped.
((byte*)ptr)[0] = ((byte*)&value)[3];
((byte*)ptr)[1] = ((byte*)&value)[2];
((byte*)ptr)[2] = ((byte*)&value)[1];
((byte*)ptr)[3] = ((byte*)&value)[0];
((byte*)ptr)[4] = ((byte*)&value)[5];
((byte*)ptr)[5] = ((byte*)&value)[4];
((byte*)ptr)[6] = ((byte*)&value)[7];
((byte*)ptr)[7] = ((byte*)&value)[6];
((int*)ptr)[2] = ((int*)&value)[2];
((int*)ptr)[3] = ((int*)&value)[3];
}
} }
private static void WriteBigEndianInteger(byte* ptr, uint value) private static void WriteBigEndianInteger(byte* ptr, uint value)
@ -402,7 +370,7 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
// The caller will not mutate it. // The caller will not mutate it.
fixed (byte* pExistingTemplate = existingTemplate) fixed (byte* pExistingTemplate = existingTemplate)
{ {
if (Read32bitAlignedGuid(&pExistingTemplate[sizeof(uint)]) == keyId) if (ReadGuid(&pExistingTemplate[sizeof(uint)]) == keyId)
{ {
return existingTemplate; return existingTemplate;
} }
@ -415,7 +383,7 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
byte[] newTemplate = (byte[])existingTemplate.Clone(); byte[] newTemplate = (byte[])existingTemplate.Clone();
fixed (byte* pNewTemplate = newTemplate) fixed (byte* pNewTemplate = newTemplate)
{ {
Write32bitAlignedGuid(&pNewTemplate[sizeof(uint)], keyId); WriteGuid(&pNewTemplate[sizeof(uint)], keyId);
if (isProtecting) if (isProtecting)
{ {
Volatile.Write(ref _aadTemplate, newTemplate); Volatile.Write(ref _aadTemplate, newTemplate);