internal static DatabaseInfo ParseHeader(SpanStream io, byte[] compositeKey) { var header = io.Read <Header>(); if (header.Signature1 != Magic1) { throw MakeInvalidFormatError($"primary file signature is invalid: {header.Signature1:x8}"); } if (!Magic2.Contains(header.Signature2)) { throw MakeInvalidFormatError($"secondary file signature is invalid: {header.Signature2:x8}"); } if (header.MajorVersion != Version4) { throw MakeUnsupportedError($"Version {header.MajorVersion}.{header.MinorVersion}"); } var info = ReadEncryptionInfo(ref io); var headerSize = io.Position; io.Rewind(); var headerBytes = io.ReadBytes(headerSize); var computedHash = Crypto.Sha256(headerBytes); var storedHash = io.ReadBytes(32); if (!Crypto.AreEqual(storedHash, computedHash)) { throw MakeInvalidFormatError("Header hash doesn't match"); } var storedHeaderMac = io.ReadBytes(32); var derivedKey = DeriveMasterKey(compositeKey, info.Kdf); var(encryptionKey, hmacKey) = Util.DeriveDatabaseKeys(derivedKey, info.Seed); var blockHmacKey = Util.ComputeBlockHmacKey(hmacKey, ulong.MaxValue); var computedHeaderMac = Crypto.HmacSha256(blockHmacKey, headerBytes); if (!Crypto.AreEqual(storedHeaderMac, computedHeaderMac)) { throw MakeInvalidFormatError("Header MAC doesn't match"); } return(new DatabaseInfo(headerSize: io.Position, isCompressed: info.IsCompressed, cipher: info.Cipher, encryptionKey: encryptionKey, iv: info.Iv, hmacKey: hmacKey)); }