diff --git a/src/ImageSharp/MetaData/Profiles/ICC/IccProfile.cs b/src/ImageSharp/MetaData/Profiles/ICC/IccProfile.cs index df85b2ab8..82f16683b 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/IccProfile.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/IccProfile.cs @@ -108,7 +108,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc #if !NETSTANDARD1_1 /// - /// Calculates the MD5 hash value of an ICC profile header + /// Calculates the MD5 hash value of an ICC profile /// /// The data of which to calculate the hash value /// The calculated hash @@ -117,22 +117,38 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc Guard.NotNull(data, nameof(data)); Guard.IsTrue(data.Length >= 128, nameof(data), "Data length must be at least 128 to be a valid profile header"); - byte[] header = new byte[128]; - Buffer.BlockCopy(data, 0, header, 0, 128); + const int profileFlagPos = 44; + const int renderingIntentPos = 64; + const int profileIdPos = 84; + + // need to copy some values because they need to be zero for the hashing + byte[] temp = new byte[24]; + Buffer.BlockCopy(data, profileFlagPos, temp, 0, 4); + Buffer.BlockCopy(data, renderingIntentPos, temp, 4, 4); + Buffer.BlockCopy(data, profileIdPos, temp, 8, 16); using (var md5 = MD5.Create()) { - // Zero out some values - Array.Clear(header, 44, 4); // Profile flags - Array.Clear(header, 64, 4); // Rendering Intent - Array.Clear(header, 84, 16); // Profile ID - - // Calculate hash - byte[] hash = md5.ComputeHash(data); - - // Read values from hash - var reader = new IccDataReader(hash); - return reader.ReadProfileId(); + try + { + // Zero out some values + Array.Clear(data, profileFlagPos, 4); + Array.Clear(data, renderingIntentPos, 4); + Array.Clear(data, profileIdPos, 16); + + // Calculate hash + byte[] hash = md5.ComputeHash(data); + + // Read values from hash + var reader = new IccDataReader(hash); + return reader.ReadProfileId(); + } + finally + { + Buffer.BlockCopy(temp, 0, data, profileFlagPos, 4); + Buffer.BlockCopy(temp, 4, data, renderingIntentPos, 4); + Buffer.BlockCopy(temp, 8, data, profileIdPos, 16); + } } } diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/IccProfileTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/IccProfileTests.cs new file mode 100644 index 000000000..f49cb6bd8 --- /dev/null +++ b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/IccProfileTests.cs @@ -0,0 +1,39 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp.MetaData.Profiles.Icc; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Icc +{ + public class IccProfileTests + { + +#if !NETSTANDARD1_1 + + [Theory] + [MemberData(nameof(IccTestDataProfiles.ProfileIdTestData), MemberType = typeof(IccTestDataProfiles))] + public void CalculateHash_WithByteArray_CalculatesProfileHash(byte[] data, IccProfileId expected) + { + IccProfileId result = IccProfile.CalculateHash(data); + + Assert.Equal(expected, result); + } + + [Fact] + public void CalculateHash_WithByteArray_DoesNotModifyData() + { + byte[] data = IccTestDataProfiles.Profile_Random_Array; + byte[] copy = new byte[data.Length]; + Buffer.BlockCopy(data, 0, copy, 0, data.Length); + + IccProfileId result = IccProfile.CalculateHash(data); + + Assert.Equal(data, copy); + } + +#endif + + } +} diff --git a/tests/ImageSharp.Tests/TestDataIcc/IccTestDataProfiles.cs b/tests/ImageSharp.Tests/TestDataIcc/IccTestDataProfiles.cs index 3cf66ffed..a5f0ce3fd 100644 --- a/tests/ImageSharp.Tests/TestDataIcc/IccTestDataProfiles.cs +++ b/tests/ImageSharp.Tests/TestDataIcc/IccTestDataProfiles.cs @@ -9,6 +9,27 @@ namespace SixLabors.ImageSharp.Tests { internal static class IccTestDataProfiles { + public static readonly IccProfileId Header_Random_Id_Value = new IccProfileId(0x84A8D460, 0xC716B6F3, 0x9B0E4C3D, 0xAB95F838); + public static readonly IccProfileId Profile_Random_Id_Value = new IccProfileId(0x917D6DE6, 0x84C958D1, 0x3BB0F5BB, 0xADD1134F); + + public static readonly byte[] Header_Random_Id_Array = + { +#if !NETSTANDARD1_1 + 0x84, 0xA8, 0xD4, 0x60, 0xC7, 0x16, 0xB6, 0xF3, 0x9B, 0x0E, 0x4C, 0x3D, 0xAB, 0x95, 0xF8, 0x38, +#else + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +#endif + }; + + public static readonly byte[] Profile_Random_Id_Array = + { +#if !NETSTANDARD1_1 + 0x91, 0x7D, 0x6D, 0xE6, 0x84, 0xC9, 0x58, 0xD1, 0x3B, 0xB0, 0xF5, 0xBB, 0xAD, 0xD1, 0x13, 0x4F, +#else + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +#endif + }; + public static readonly IccProfileHeader Header_Random_Write = CreateHeaderRandomValue( 562, // should be overwritten new IccProfileId(1, 2, 3, 4), // should be overwritten @@ -16,20 +37,13 @@ namespace SixLabors.ImageSharp.Tests public static readonly IccProfileHeader Header_Random_Read = CreateHeaderRandomValue(132, #if !NETSTANDARD1_1 - new IccProfileId(2931428592, 418415738, 3086756963, 2237536530), + Header_Random_Id_Value, #else IccProfileId.Zero, #endif "acsp"); - public static readonly byte[] Header_Random_Array = CreateHeaderRandomArray(132, 0, new byte[] - { -#if !NETSTANDARD1_1 - 0xAE, 0xBA, 0x0C, 0xF0, 0x18, 0xF0, 0x84, 0x7A, 0xB7, 0xFC, 0x2C, 0x63, 0x85, 0x5E, 0x19, 0x12, -#else - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -#endif - }); + public static readonly byte[] Header_Random_Array = CreateHeaderRandomArray(132, 0, Header_Random_Id_Array); public static IccProfileHeader CreateHeaderRandomValue(uint size, IccProfileId id, string fileSignature) { @@ -45,11 +59,7 @@ namespace SixLabors.ImageSharp.Tests DeviceModel = 987654321u, FileSignature = "acsp", Flags = IccProfileFlag.Embedded | IccProfileFlag.Independent, -#if !NETSTANDARD1_1 - Id = new IccProfileId(2931428592, 418415738, 3086756963, 2237536530), -#else - Id = IccProfileId.Zero, -#endif + Id = id, PcsIlluminant = new Vector3(4, 5, 6), PrimaryPlatformSignature = IccPrimaryPlatformType.MicrosoftCorporation, ProfileConnectionSpace = IccColorSpaceType.CieXyz, @@ -94,14 +104,7 @@ namespace SixLabors.ImageSharp.Tests }); } - public static byte[] Profile_Random_Array = ArrayHelper.Concat(CreateHeaderRandomArray(168, 2, new byte[] - { -#if !NETSTANDARD1_1 - 0xA9, 0x71, 0x8F, 0xC1, 0x1E, 0x2D, 0x64, 0x1B, 0x10, 0xF4, 0x7D, 0x6A, 0x5B, 0xF6, 0xAC, 0xB9 -#else - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -#endif - }), + public static byte[] Profile_Random_Array = ArrayHelper.Concat(CreateHeaderRandomArray(168, 2, Profile_Random_Id_Array), new byte[] { 0x00, 0x00, 0x00, 0x00, // tag signature (Unknown) @@ -118,7 +121,7 @@ namespace SixLabors.ImageSharp.Tests public static IccProfile Profile_Random_Val = new IccProfile(CreateHeaderRandomValue(168, #if !NETSTANDARD1_1 - new IccProfileId(0xA9718FC1, 0x1E2D641B, 0x10F47D6A, 0x5BF6ACB9), + Profile_Random_Id_Value, #else IccProfileId.Zero, #endif @@ -128,5 +131,11 @@ namespace SixLabors.ImageSharp.Tests IccTestDataTagDataEntry.Unknown_Val, IccTestDataTagDataEntry.Unknown_Val }); + + public static object[][] ProfileIdTestData = + { + new object[] { Header_Random_Array, Header_Random_Id_Value }, + new object[] { Profile_Random_Array, Profile_Random_Id_Value }, + }; } }