diff --git a/src/KeyKeeper/PasswordStore/Crypto/PassStoreContentChunk.cs b/src/KeyKeeper/PasswordStore/Crypto/PassStoreContentChunk.cs index a4ac43c..6d5301f 100644 --- a/src/KeyKeeper/PasswordStore/Crypto/PassStoreContentChunk.cs +++ b/src/KeyKeeper/PasswordStore/Crypto/PassStoreContentChunk.cs @@ -1,4 +1,5 @@ using System; +using System.Linq; using System.Buffers.Binary; using System.IO; using System.Security.Cryptography; @@ -37,7 +38,7 @@ public class PassStoreContentChunk try { chunkLen = rd.ReadUInt16(); - chunkLen = (chunkLen << 8) | rd.ReadByte(); + chunkLen = chunkLen | (rd.ReadByte() << 16); } catch (EndOfStreamException) { @@ -52,16 +53,28 @@ public class PassStoreContentChunk byte[] storedHmac = new byte[HMAC_SIZE]; str.Read(storedHmac, 0, HMAC_SIZE); - HMACSHA3_512 hmac = new(key); - hmac.TransformBlock(chunk, (int)str.Position, Math.Min(chunkLen, chunk.Length - (int)str.Position), null, 0); + SHA3_512 hasher = SHA3_512.Create(); + byte[] innerKey = key.Select(x => (byte)(x ^ 0x36)).ToArray(); + byte[] outerKey = key.Select(x => (byte)(x ^ 0x5c)).ToArray(); + + hasher.TransformBlock(innerKey, 0, innerKey.Length, null, 0); + Array.Fill(innerKey, 0); // erase key after use + + hasher.TransformBlock(chunk, (int)str.Position, chunk.Length - (int)str.Position, null, 0); byte[] encodedOrdinal = new byte[sizeof(int)]; BinaryPrimitives.WriteInt32LittleEndian(new Span(encodedOrdinal), chunkOrdinal); - hmac.TransformBlock(encodedOrdinal, 0, encodedOrdinal.Length, null, 0); - - byte[] actualHmac = hmac.Hash!; - if (!storedHmac.Equals(actualHmac)) + hasher.TransformFinalBlock(encodedOrdinal, 0, encodedOrdinal.Length); + byte[] innerHash = hasher.Hash!; + + hasher = SHA3_512.Create(); + hasher.TransformBlock(outerKey, 0, outerKey.Length, null, 0); + Array.Fill(outerKey, 0); + hasher.TransformFinalBlock(innerHash, 0, innerHash.Length); + byte[] actualHmac = hasher.Hash!; + + if (!storedHmac.SequenceEqual(actualHmac)) { throw PassStoreFileException.ContentHMACMismatch; }