Beispiel #1
0
 public void CheckE(string testName, byte[] key, byte[] data, byte[] result)
 {
     algo     = new HMACSHA256();
     algo.Key = key;
     byte [] copy = new byte [data.Length];
     // LAMESPEC or FIXME: TransformFinalBlock doesn't return HashValue !
     for (int i = 0; i < data.Length - 1; i++)
     {
         algo.TransformBlock(data, i, 1, copy, i);
     }
     algo.TransformFinalBlock(data, data.Length - 1, 1);
     AreEqual(result, algo.Hash, testName + "e");
 }
Beispiel #2
0
        private void WriteSafeBlock()
        {
            byte[] pbBlockIndex = MemUtil.UInt64ToBytes(m_uBlockIndex);

            int cbBlockSize = m_iBufferPos;

            byte[] pbBlockSize = MemUtil.Int32ToBytes(cbBlockSize);

            byte[] pbBlockHmac;
            byte[] pbBlockKey = GetHmacKey64(m_pbKey, m_uBlockIndex);
            using (HMACSHA256 h = new HMACSHA256(pbBlockKey))
            {
                h.TransformBlock(pbBlockIndex, 0, pbBlockIndex.Length,
                                 pbBlockIndex, 0);
                h.TransformBlock(pbBlockSize, 0, pbBlockSize.Length,
                                 pbBlockSize, 0);

                if (cbBlockSize != 0)
                {
                    h.TransformBlock(m_pbBuffer, 0, cbBlockSize, m_pbBuffer, 0);
                }

                h.TransformFinalBlock(MemUtil.EmptyByteArray, 0, 0);

                pbBlockHmac = h.Hash;
            }
            MemUtil.ZeroByteArray(pbBlockKey);

            MemUtil.Write(m_sBase, pbBlockHmac);
            // MemUtil.Write(m_sBase, pbBlockIndex); // Implicit
            MemUtil.Write(m_sBase, pbBlockSize);
            if (cbBlockSize != 0)
            {
                m_sBase.Write(m_pbBuffer, 0, cbBlockSize);
            }

            ++m_uBlockIndex;
            m_iBufferPos = 0;
        }
Beispiel #3
0
        private static (byte, byte[]) EncryptData(Stream inStream, Stream outStream, byte[] internalKey, byte[] iv, int bufferSize)
        {
            var lastDataReadSize = 0; // File size modulo 16 in least significant byte positions

            using (var cipher = CreateAes(internalKey, iv))
                using (var ms = new MemoryStream())
                    using (var cryptoStream = new CryptoStream(ms, cipher.CreateEncryptor(), CryptoStreamMode.Write))
                    {
                        int bytesRead;
                        var buffer = new byte[bufferSize];
                        while ((bytesRead = inStream.Read(buffer, 0, buffer.Length)) > 0)
                        {
                            cryptoStream.Write(buffer, 0, bytesRead);
                            if (bytesRead < bufferSize)
                            {
                                lastDataReadSize = bytesRead % AesBlockSize;
                                if (lastDataReadSize != 0)
                                {
                                    var padLen  = 16 - bytesRead % AesBlockSize;
                                    var padding = new byte[padLen];
                                    padding.Fill((byte)padLen);
                                    cryptoStream.Write(padding, 0, padding.Length);
                                }
                            }
                        }

                        cryptoStream.FlushFinalBlock();
                        ms.Position = 0;

                        using (var hmac0 = new HMACSHA256(internalKey))
                        {
                            hmac0.Initialize();

                            while ((bytesRead = ms.Read(buffer, 0, buffer.Length)) > 0)
                            {
                                outStream.Write(buffer, 0, bytesRead);
                                hmac0.TransformBlock(buffer, 0, bytesRead, null, 0);
                            }

                            hmac0.TransformFinalBlock(Array.Empty <byte>(), 0, 0);

                            return((byte)lastDataReadSize, hmac0.Hash);
                        }
                    }
        }
Beispiel #4
0
        private void OnBufferReady(byte[] buffer)
        {
            if (!_secure)
            {
                ProcessFrame(buffer, _offset, _bytesToBuffer);
                return;
            }

            if (_bytesToBuffer == PREFIX_SIZE)
            {
                byte[] size = new byte[2];

                _decryptor.TransformBlock(_buffer, _offset + HMAC_SIZE, 2, size, 0);
                _bytesToBuffer += BitConverter.ToUInt16(size, 0);
            }
            else
            {
                byte[] counter = BitConverter.GetBytes(_inCounter++);

                _verifier.TransformBlock(counter, 0, counter.Length, null, 0);
                _verifier.TransformFinalBlock(_buffer, _offset + HMAC_SIZE, _bytesToBuffer - HMAC_SIZE);

                if (!CompareBytes(_verifier.Hash, 0, _buffer, _offset, HMAC_SIZE))
                {
                    OnExceptionThrown(new InvalidDataException("Invalid record signature."));
                    return;
                }

                _verifier.Initialize();
                _decryptor.TransformBlock(_buffer, _offset + PREFIX_SIZE, _bytesToBuffer - PREFIX_SIZE, _buffer, _offset + PREFIX_SIZE);

                if (_state == FrameState.Established)
                {
                    OnDataDecoded(buffer, _offset + PREFIX_SIZE, _bytesToBuffer - PREFIX_SIZE);

                    _bufferPosition = 0;
                    _bytesToBuffer  = PREFIX_SIZE;
                }
                else
                {
                    ProcessFrame(buffer, _offset + PREFIX_SIZE, _bytesToBuffer - PREFIX_SIZE);
                }
            }
        }
        private static string CalculateSha256Hash(string key, string msg1, string msg2)
        {
            byte[] encodedKey  = Encoding.UTF8.GetBytes(key);
            byte[] encodedMsg1 = Encoding.UTF8.GetBytes(msg1);
            byte[] encodedMsg2 = Encoding.UTF8.GetBytes(msg2);

            HMACSHA256 hmac = new HMACSHA256(encodedKey);

            hmac.TransformBlock(encodedMsg1, 0, encodedMsg1.Length, null, 0);
            hmac.TransformFinalBlock(encodedMsg2, 0, encodedMsg2.Length);

            StringBuilder sb = new StringBuilder();

            foreach (byte b in hmac.Hash)
            {
                sb.Append(b.ToString("x2"));
            }
            return(sb.ToString());
        }
Beispiel #6
0
        public static byte[] ComputeHMACSHA256HashFromFile(string filePath, byte[] authKey, long startPosition, long endPosition)
        {
            byte[] hash = null;

            using (var fStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.None))
            {
                fStream.Position = startPosition;
                byte[] buffer = new byte[(1024 * 4)];
                long   amount = (endPosition - startPosition);

                using (var hmacSha256 = new HMACSHA256(authKey))
                {
                    while (amount > 0)
                    {
                        int bytesRead = fStream.Read(buffer, 0, (int)Math.Min(buffer.Length, amount));

                        if (bytesRead > 0)
                        {
                            amount -= bytesRead;

                            if (amount > 0)
                            {
                                hmacSha256.TransformBlock(buffer, 0, bytesRead, buffer, 0);
                            }
                            else
                            {
                                hmacSha256.TransformFinalBlock(buffer, 0, bytesRead);
                            }
                        }
                        else
                        {
                            throw new InvalidOperationException();
                        }
                    }

                    hash = hmacSha256.Hash;
                }
            }

            return(hash);
        }
Beispiel #7
0
        private string Sign <T>(string key, Message <T> ioPubMessage, List <string> messages, NetMQSocket iopub)
        {
            var encoder = new UTF8Encoding();
            var hMAC    = new HMACSHA256(encoder.GetBytes(key));

            hMAC.Initialize();

            // https://jupyter-client.readthedocs.io/en/stable/messaging.html#the-wire-protocol

            messages.Add(JsonConvert.SerializeObject(ioPubMessage.Header));
            messages.Add(JsonConvert.SerializeObject(ioPubMessage.ParentHeader));
            messages.Add(JsonConvert.SerializeObject(ioPubMessage.Metadata));
            messages.Add(JsonConvert.SerializeObject(ioPubMessage.Content));

            // signature
            foreach (string item in messages)
            {
                var sourceBytes = encoder.GetBytes(item);
                hMAC.TransformBlock(sourceBytes, 0, sourceBytes.Length, null, 0);
            }

            hMAC.TransformFinalBlock(new byte[0], 0, 0);
            return(BitConverter.ToString(hMAC.Hash).Replace("-", "").ToLower());
        }
        private bool ReadSafeBlock()
        {
            if (m_bEos)
            {
                return(false);                   // End of stream reached already
            }
            byte[] pbStoredHmac = MemUtil.Read(m_sBase, 32);
            if ((pbStoredHmac == null) || (pbStoredHmac.Length != 32))
            {
                throw new EndOfStreamException(KLRes.FileCorrupted + " " +
                                               KLRes.FileIncomplete);
            }

            // Block index is implicit: it's used in the HMAC computation,
            // but does not need to be stored
            // byte[] pbBlockIndex = MemUtil.Read(m_sBase, 8);
            // if((pbBlockIndex == null) || (pbBlockIndex.Length != 8))
            //	throw new EndOfStreamException();
            // ulong uBlockIndex = MemUtil.BytesToUInt64(pbBlockIndex);
            // if((uBlockIndex != m_uBlockIndex) && m_bVerify)
            //	throw new InvalidDataException();
            byte[] pbBlockIndex = MemUtil.UInt64ToBytes(m_uBlockIndex);

            byte[] pbBlockSize = MemUtil.Read(m_sBase, 4);
            if ((pbBlockSize == null) || (pbBlockSize.Length != 4))
            {
                throw new EndOfStreamException(KLRes.FileCorrupted + " " +
                                               KLRes.FileIncomplete);
            }
            int nBlockSize = MemUtil.BytesToInt32(pbBlockSize);

            if (nBlockSize < 0)
            {
                throw new InvalidDataException(KLRes.FileCorrupted);
            }

            m_iBufferPos = 0;

            SetBuffer(MemUtil.Read(m_sBase, nBlockSize));
            if ((m_pbBuffer == null) || ((m_pbBuffer.Length != nBlockSize) && m_bVerify))
            {
                throw new EndOfStreamException(KLRes.FileCorrupted + " " +
                                               KLRes.FileIncompleteExpc);
            }

            if (m_bVerify)
            {
                byte[] pbCmpHmac;
                byte[] pbBlockKey = GetHmacKey64(m_pbKey, m_uBlockIndex);
                using (HMACSHA256 h = new HMACSHA256(pbBlockKey))
                {
                    h.TransformBlock(pbBlockIndex, 0, pbBlockIndex.Length,
                                     pbBlockIndex, 0);
                    h.TransformBlock(pbBlockSize, 0, pbBlockSize.Length,
                                     pbBlockSize, 0);

                    if (m_pbBuffer.Length != 0)
                    {
                        h.TransformBlock(m_pbBuffer, 0, m_pbBuffer.Length,
                                         m_pbBuffer, 0);
                    }

                    h.TransformFinalBlock(MemUtil.EmptyByteArray, 0, 0);

                    pbCmpHmac = h.Hash;
                }
                MemUtil.ZeroByteArray(pbBlockKey);

                if (!MemUtil.ArraysEqual(pbCmpHmac, pbStoredHmac))
                {
                    throw new InvalidDataException(KLRes.FileCorrupted);
                }
            }

            ++m_uBlockIndex;

            if (nBlockSize == 0)
            {
                m_bEos = true;
                return(false);                // No further data available
            }
            return(true);
        }
    /// <summary>Encrypts files using a key or the supplied password.</summary>
    /// <remarks>
    /// The original file is not modified; a new encrypted file is created.
    /// The password is not required if a master key has been set
    /// (either with `RandomKeyGgen` or with `SetMasterKey`).
    /// If a password is supplied, it will be used to create a key with PBKDF2.
    /// </remarks>
    /// <param name="path">The file path.</param>
    /// <param name="password">Optional, the pasword.</param>
    /// <returns>The encrypted file path.</returns>
    public string EncryptFile(string path, string password = null)
    {
        byte[] salt = RandomBytes(saltLen);
        byte[] iv   = RandomBytes(ivLen);
        try
        {
            byte[][] keys    = this.Keys(salt, password);
            byte[]   aesKey  = keys[0], macKey = keys[1];
            string   newPath = path + ".enc";

            using (FileStream fs = new FileStream(newPath, FileMode.Create, FileAccess.Write))
            {
                fs.Write(salt, 0, saltLen);
                fs.Write(iv, 0, ivLen);

                RijndaelManaged  cipher = this.Cipher(aesKey, iv);
                ICryptoTransform ict    = cipher.CreateEncryptor();
                HMACSHA256       hmac   = new HMACSHA256(macKey);

                hmac.TransformBlock(iv, 0, iv.Length, null, 0);

                foreach (Object[] chunk in FileChunks(path))
                {
                    byte[] data       = (byte[])chunk[0];
                    byte[] ciphertext = new byte[data.Length];

                    if ((bool)chunk[1])
                    {
                        ciphertext = ict.TransformFinalBlock(data, 0, data.Length);
                    }
                    else
                    {
                        ict.TransformBlock(data, 0, data.Length, ciphertext, 0);
                    }

                    hmac.TransformBlock(ciphertext, 0, ciphertext.Length, null, 0);
                    fs.Write(ciphertext, 0, ciphertext.Length);
                }
                hmac.TransformFinalBlock(new byte[0], 0, 0);
                byte[] mac = hmac.Hash;
                fs.Write(mac, 0, mac.Length);

                ict.Dispose();
                cipher.Dispose();
                hmac.Dispose();
            }
            return(newPath);
        }
        catch (ArgumentException e)
        {
            this.ErrorHandler(e);
        }
        catch (CryptographicException e)
        {
            this.ErrorHandler(e);
        }
        catch (UnauthorizedAccessException e)
        {
            this.ErrorHandler(e);
        }
        catch (IOException e)
        {
            this.ErrorHandler(e);
        }
        return(null);
    }
Beispiel #10
0
        private static void DecryptData(Stream inStream, Stream outStream, byte[] key, byte[] ivMain, byte[] mainKeyAndIv, int bufferSize)
        {
            var(dataIv, internalKey) = DecryptMainKeyAndIv(key, ivMain, mainKeyAndIv);
            var currentPosition          = inStream.Position;
            var endPositionEncryptedData = inStream.Length - 32 - 1;

            // Get padding and hmac
            inStream.Position = endPositionEncryptedData;
            var padding           = (16 - ReadBytes(inStream, 1)[0]) % 16;
            var hmacEncryptedData = ReadBytes(inStream, 32);

            // Reset the position to the beginning of the encrypted data
            inStream.Position = currentPosition;

            // Get hmac
            using (var hmac0 = new HMACSHA256(internalKey))
            {
                hmac0.Initialize();

                // Get the cipher
                using (var cipher = CreateAes(internalKey, dataIv))
                    using (var decrypter = cipher.CreateDecryptor())
                    {
                        // First read as much data as possible.
                        ReadEncryptedBytes(bufferSize);

                        // read the remaining
                        ReadEncryptedBytes();

                        // Everything read but the last block need to remove padding
                        if (inStream.Position != endPositionEncryptedData)
                        {
                            var lastBlock = ReadBytes(inStream, AesBlockSize);
                            hmac0.TransformBlock(lastBlock, 0, lastBlock.Length, null, 0);

                            decrypter.TransformBlock(lastBlock, 0, lastBlock.Length, lastBlock, 0);
                            outStream.Write(lastBlock, 0, lastBlock.Length - padding);
                        }

                        decrypter.TransformFinalBlock(Array.Empty <byte>(), 0, 0);
                        hmac0.TransformFinalBlock(Array.Empty <byte>(), 0, 0);
                        if (!hmac0.Hash.SequenceEqual(hmacEncryptedData))
                        {
                            throw new InvalidOperationException(Resources.TheFileIsCorrupt);
                        }


                        // Functions
                        void ReadEncryptedBytes(int bytesToRead = AesBlockSize)
                        {
                            var buffer = new byte[bytesToRead];

                            while (inStream.Position < endPositionEncryptedData - bytesToRead)
                            {
                                var bytesRead = inStream.Read(buffer, 0, buffer.Length);
                                hmac0.TransformBlock(buffer, 0, bytesRead, null, 0);
                                decrypter.TransformBlock(buffer, 0, bytesRead, buffer, 0);
                                outStream.Write(buffer, 0, buffer.Length);
                            }
                        }
                    }
            }
        }
Beispiel #11
0
        public static Document Load(Stream stream, byte[] passphraseBuffer)
        {
            if (stream == null)
            {
                throw new ArgumentNullException(nameof(stream), "Stream cannot be null.");
            }
            if (passphraseBuffer == null)
            {
                throw new ArgumentNullException(nameof(passphraseBuffer), "Passphrase cannot be null.");
            }

            var buffer = new byte[16384];

            using (var ms = new MemoryStream()) {
                int read;
                while ((read = stream.Read(buffer, 0, buffer.Length)) > 0)
                {
                    ms.Write(buffer, 0, read);
                }
                buffer = ms.ToArray();
            }

            if ((buffer.Length < 200) ||
                (BitConverter.ToInt32(buffer, 0) != Tag) ||
                (BitConverter.ToInt32(buffer, buffer.Length - 32 - 16) != Tag) ||
                (BitConverter.ToInt32(buffer, buffer.Length - 32 - 12) != TagEof) ||
                (BitConverter.ToInt32(buffer, buffer.Length - 32 - 8) != Tag) ||
                (BitConverter.ToInt32(buffer, buffer.Length - 32 - 4) != TagEof))
            {
                throw new FormatException("Unrecognized file format.");
            }

            var salt = new byte[32];

            Buffer.BlockCopy(buffer, 4, salt, 0, salt.Length);

            var iter = BitConverter.ToUInt32(buffer, 36);

            byte[] stretchedKey = null, keyK = null, keyL = null, data = null;
            try {
                stretchedKey = GetStretchedKey(passphraseBuffer, salt, iter);
                if (!AreBytesTheSame(GetSha256Hash(stretchedKey), buffer, 40))
                {
                    throw new CryptographicException("Password mismatch.");
                }

                keyK = DecryptKey(stretchedKey, buffer, 72);
                keyL = DecryptKey(stretchedKey, buffer, 104);

                var iv = new byte[16];
                Buffer.BlockCopy(buffer, 136, iv, 0, iv.Length);

                data = DecryptData(keyK, iv, buffer, 152, buffer.Length - 200);

                using (var dataHash = new HMACSHA256(keyL)) {
                    var dataOffset = 0;

                    var headerFields = new List <Header>();
                    while (dataOffset < data.Length)
                    {
                        var fieldLength     = BitConverter.ToInt32(data, dataOffset + 0);
                        var fieldLengthFull = ((fieldLength + 5 - 1) / 16 + 1) * 16;
                        var fieldType       = (HeaderType)data[dataOffset + 4];
                        var fieldData       = new byte[fieldLength];
                        try {
                            Buffer.BlockCopy(data, dataOffset + 5, fieldData, 0, fieldLength);
                            dataOffset += fieldLengthFull;                                    //there is ALWAYS some random bytes added, thus extra block if 16 bytes

                            dataHash.TransformBlock(fieldData, 0, fieldData.Length, null, 0); //not hashing length nor type - wtf?
                            if (fieldType == HeaderType.EndOfEntry)
                            {
                                break;
                            }

                            headerFields.Add(new Header(fieldType, fieldData));
                        } finally {
                            Array.Clear(fieldData, 0, fieldData.Length);
                        }
                    }

                    if ((headerFields.Count == 0) || (headerFields[0].Version < 0x0300))
                    {
                        throw new FormatException("Unrecognized file format version.");
                    }

                    var           recordFields = new List <List <Record> >();
                    List <Record> records      = null;
                    while (dataOffset < data.Length)
                    {
                        var fieldLength     = BitConverter.ToInt32(data, dataOffset + 0);
                        var fieldLengthFull = ((fieldLength + 5 - 1) / 16 + 1) * 16;
                        var fieldType       = (RecordType)data[dataOffset + 4];
                        var fieldData       = new byte[fieldLength];
                        try {
                            Buffer.BlockCopy(data, dataOffset + 5, fieldData, 0, fieldLength);
                            dataOffset += fieldLengthFull;                                    //there is ALWAYS some random bytes added, thus extra block if 16 bytes

                            dataHash.TransformBlock(fieldData, 0, fieldData.Length, null, 0); //not hashing length nor type - wtf?
                            if (fieldType == RecordType.EndOfEntry)
                            {
                                records = null; continue;
                            }

                            if (records == null)
                            {
                                records = new List <Record>();
                                recordFields.Add(records);
                            }
                            records.Add(new Record(fieldType, fieldData));
                        } finally {
                            Array.Clear(fieldData, 0, fieldData.Length);
                        }
                    }

                    dataHash.TransformFinalBlock(new byte[] { }, 0, 0);

                    if (!AreBytesTheSame(dataHash.Hash, buffer, buffer.Length - 32))
                    {
                        throw new CryptographicException("Authentication mismatch.");
                    }

                    var document = new Document(headerFields, recordFields.ToArray())
                    {
                        _iterations = (int)iter,       //to avoid rounding up if iteration count is less than 2048
                        Passphrase  = passphraseBuffer //to avoid keeping password in memory for save - at least we don't need to deal with string's immutability.
                    };
                    return(document);
                }
            } catch (CryptographicException ex) {
                throw new FormatException(ex.Message, ex);
            } finally { //best effort to sanitize memory
                if (stretchedKey != null)
                {
                    Array.Clear(stretchedKey, 0, stretchedKey.Length);
                }
                if (keyK != null)
                {
                    Array.Clear(keyK, 0, keyK.Length);
                }
                if (keyL != null)
                {
                    Array.Clear(keyL, 0, keyL.Length);
                }
                if (data != null)
                {
                    Array.Clear(data, 0, data.Length);
                }
            }
        }
Beispiel #12
0
        private void button2_Click(object sender, EventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog();

            if (ofd.ShowDialog() == DialogResult.OK)
            {
                SaveFileDialog sfd = new SaveFileDialog();

                if (sfd.ShowDialog() == DialogResult.OK)
                {
                    try
                    {
                        byte[] AES_KEY =
                        {
                            0xB4, 0xEB, 0x7F, 0xB9,
                            0xA9, 0xAC, 0x05, 0x92,
                            0xAB, 0x8E, 0x42, 0xAB,
                            0xCC, 0x61, 0xD0, 0xC0
                        };

                        byte[] HMAC_KEY =
                        {
                            0x82, 0xF4, 0x1A, 0x4F,
                            0x03, 0x45, 0x65, 0x01,
                            0x2F, 0x73, 0x9C, 0x4D,
                            0x8E, 0x63, 0xA9, 0x1F,
                            0xB9, 0x2B, 0x9D, 0x4E,
                            0xE1, 0x11, 0x91, 0x47,
                            0xD4, 0x3C, 0xC6, 0x88,
                            0x3F, 0xC7, 0xFC, 0x70
                        };

                        BinaryReader reader = new BinaryReader(new FileStream(ofd.FileName, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite));
                        BinaryWriter writer = new BinaryWriter(new FileStream(sfd.FileName, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite));

                        byte[] ProfileID = HexStringToByteArray(textBox1.Text);

                        if (reader.ReadUInt32() != 0x73736D63)
                        {
                            throw new Exception("Invalid File");
                        }

                        uint ReadSize = BSwapUint(reader.ReadUInt32());

                        byte[] Hash        = reader.ReadBytes(0x20);
                        uint   DataSizeEnc = BSwapUint(reader.ReadUInt32());
                        byte[] AES_IV      = reader.ReadBytes(0x10);

                        uint   DataSizeDec = DecryptDataSize(AES_IV, DataSizeEnc);
                        byte[] DataBuffer  = reader.ReadBytes((int)(DataSizeDec));

                        XorAes(ref AES_KEY, ProfileID, 1);

                        AesCryptoServiceProvider aes = new AesCryptoServiceProvider();
                        aes.Mode    = CipherMode.CBC;
                        aes.Padding = PaddingMode.None;

                        byte[] Encrypted = aes.CreateEncryptor(AES_KEY, AES_IV).TransformFinalBlock(DataBuffer, 0, DataBuffer.Length);

                        XorHmac(ref HMAC_KEY, ProfileID, 0);
                        HMACSHA256 hmac = new HMACSHA256(HMAC_KEY);

                        byte[] Tmp = new byte[0x14];
                        Buffer.BlockCopy(BitConverter.GetBytes(BSwapUint(DataSizeEnc)), 0, Tmp, 0, 4);
                        Buffer.BlockCopy(AES_IV, 0, Tmp, 4, 0x10);

                        hmac.TransformBlock(Tmp, 0, 0x14, null, 0);
                        hmac.TransformFinalBlock(Encrypted, 0, Encrypted.Length);

                        writer.Write(0x73736D63);
                        writer.Write(BSwapUint(ReadSize));
                        writer.Write(hmac.Hash);
                        writer.Write(BSwapUint(DataSizeEnc));
                        writer.Write(AES_IV);
                        writer.Write(Encrypted);

                        writer.Flush();
                        writer.Close();
                        reader.Close();
                    }
                    catch (Exception x)
                    {
                        MessageBox.Show(x.Message);
                    }
                }
            }
        }
Beispiel #13
0
        public static byte[] Pbkdf2(byte[] password, byte[] salt, int iterations = Pbkdf2Iterations)
        {
            /*
             * // Algorithm Credits to https://github.com/vexocide
             * //
             * // Implements PBKDF2WithHmacSHA256 in Java. Beautifully Amazing.
             * using (var mac = new HMACSHA256(password))
             * {
             *  mac.TransformBlock(salt, 0, salt.Length, salt, 0);
             *  byte[] i = { 0, 0, 0, 1 };
             *  mac.TransformFinalBlock(i, 0, i.Length);
             *  byte[] t = mac.Hash;
             *  mac.Initialize();
             *
             *  byte[] u = t;
             *  for (uint c = 2; c <= iterations; c++)
             *  {
             *      t = mac.ComputeHash(t);
             *      for (int j = 0; j < mac.HashSize / 8; j++)
             *      {
             *          u[j] ^= t[j];
             *      }
             *  }
             *
             *  return u;
             * }
             */
#if STANDARD
            using (var macSalt = IncrementalHash.CreateHMAC(HashAlgorithmName.SHA256, password))
#endif
            using (var mac = new HMACSHA256(password))
            {
#if STANDARD
                macSalt.AppendData(salt);
#else
                mac.TransformBlock(salt, 0, salt.Length, salt, 0);
#endif
                byte[] i = { 0, 0, 0, 1 };
#if STANDARD
                macSalt.AppendData(i);
#else
                mac.TransformFinalBlock(i, 0, i.Length);
#endif
#if STANDARD
                byte[] t = macSalt.GetHashAndReset();
#else
                byte[] t = mac.Hash;
                mac.Initialize();
#endif

                byte[] u = t;

                for (uint c = 2; c <= iterations; c++)
                {
                    t = mac.ComputeHash(t);
                    for (int j = 0; j < mac.HashSize / 8; j++)
                    {
                        u[j] ^= t[j];
                    }
                }

                return(u);
            }
        }
Beispiel #14
0
        private void InternalEnqueue(IBufferWriter <byte> bufferWriter, Action <IBufferWriter <byte> > action)
        {
            using var hub = new Hub();

            action.Invoke(hub.Writer);
            hub.Writer.Complete();

            var sequence = hub.Reader.GetSequence();

            try
            {
                if (_version.HasFlag(OmniSecureConnectionVersion.Version1) && _infoV1 != null)
                {
                    if (_infoV1.CryptoAlgorithm.HasFlag(V1.Internal.CryptoAlgorithm.Aes_256) &&
                        _infoV1.HashAlgorithm.HasFlag(V1.Internal.HashAlgorithm.Sha2_256))
                    {
                        const int headerSize = 8;
                        const int blockSize  = 16;

                        // 送信済みデータ + 送信するデータのサイズを書き込む
                        {
                            var paddingSize = blockSize;

                            if (sequence.Length % blockSize != 0)
                            {
                                paddingSize = blockSize - (int)(sequence.Length % blockSize);
                            }

                            var encryptedContentLength = blockSize + (sequence.Length + paddingSize);

                            BinaryPrimitives.TryWriteUInt64BigEndian(bufferWriter.GetSpan(headerSize), (ulong)(_totalSentSize + encryptedContentLength));
                            bufferWriter.Advance(headerSize);
                        }

                        using (var hmac = new HMACSHA256(_infoV1.MyHmacKey))
                            using (var aes = Aes.Create())
                            {
                                aes.KeySize = 256;
                                aes.Mode    = CipherMode.CBC;
                                aes.Padding = PaddingMode.PKCS7;

                                // IVを書き込む
                                var iv = new byte[blockSize];
                                _random.GetBytes(iv);
                                bufferWriter.Write(iv);
                                hmac.TransformBlock(iv, 0, iv.Length, null, 0);
                                Interlocked.Add(ref _totalSentSize, iv.Length);

                                // 暗号化データを書き込む
                                using (var encryptor = aes.CreateEncryptor(_infoV1.MyCryptoKey, iv))
                                {
                                    var inBuffer  = _bufferPool.GetArrayPool().Rent(blockSize);
                                    var outBuffer = _bufferPool.GetArrayPool().Rent(blockSize);

                                    try
                                    {
                                        while (sequence.Length > blockSize)
                                        {
                                            sequence.Slice(0, blockSize).CopyTo(inBuffer.AsSpan(0, blockSize));

                                            var transed = encryptor.TransformBlock(inBuffer, 0, blockSize, outBuffer, 0);
                                            bufferWriter.Write(outBuffer.AsSpan(0, transed));
                                            hmac.TransformBlock(outBuffer, 0, transed, null, 0);
                                            Interlocked.Add(ref _totalSentSize, transed);

                                            sequence = sequence.Slice(blockSize);
                                        }

                                        {
                                            int remain = (int)sequence.Length;
                                            sequence.CopyTo(inBuffer.AsSpan(0, remain));

                                            var remainBuffer = encryptor.TransformFinalBlock(inBuffer, 0, remain);
                                            bufferWriter.Write(remainBuffer);
                                            hmac.TransformBlock(remainBuffer, 0, remainBuffer.Length, null, 0);
                                            Interlocked.Add(ref _totalSentSize, remainBuffer.Length);
                                        }
                                    }
                                    finally
                                    {
                                        _bufferPool.GetArrayPool().Return(inBuffer);
                                        _bufferPool.GetArrayPool().Return(outBuffer);
                                    }
                                }

                                // HMACを書き込む
                                hmac.TransformFinalBlock(Array.Empty <byte>(), 0, 0);
                                bufferWriter.Write(hmac.Hash);
                            }

                        hub.Reader.Complete();

                        return;
                    }
                }
            }
            catch (OmniSecureConnectionException e)
            {
                throw e;
            }
            catch (Exception e)
            {
                throw new OmniSecureConnectionException(e.Message, e);
            }

            throw new OmniSecureConnectionException("Conversion failed.");
        }
        public override int Read(byte[] buffer, int offset, int count)
        {
            // Start new block
            if (_blockMac == null)
            {
                _blockMac = _baseStream.ReadExact(32);

                // To avoid allocations we read into the cache buffer since it's unused at this point and parse from it
                _baseStream.ReadExact(_buffer, 0, 4);
                _blockSize = BitConverter.ToInt32(_buffer, 0);

                // End of stream
                if (_blockSize == 0)
                {
                    return(0);
                }

                _blockReadPointer  = 0;
                _bufferActualSize  = 0;
                _bufferReadPointer = 0;

                // Start the MAC calculation for the new block
                _hmac = new HMACSHA256(Util.ComputeBlockHmacKey(_hmacKey, _blockIndex));

                // To avoid allocation we write into the cache buffer and then use it for hashing
                var forHashing = new OutputSpanStream(_buffer);
                forHashing.WriteUInt64(_blockIndex);
                forHashing.WriteInt32(_blockSize);
                _hmac.TransformBlock(_buffer, 0, forHashing.Position, null, 0);
            }

            // Read next piece into the buffer
            if (_bufferReadPointer >= _bufferActualSize)
            {
                var toRead = Math.Min(_buffer.Length, _blockSize - _blockReadPointer);
                _bufferActualSize  = _baseStream.Read(_buffer, 0, toRead);
                _bufferReadPointer = 0;

                // Hash the read portion
                _hmac.TransformBlock(_buffer, 0, _bufferActualSize, null, 0);
            }

            var toCopy = Math.Min(count, _bufferActualSize - _bufferReadPointer);

            Array.Copy(_buffer, _bufferReadPointer, buffer, offset, toCopy);
            _bufferReadPointer += toCopy;
            _blockReadPointer  += toCopy;

            // End of block
            if (_blockReadPointer >= _blockSize)
            {
                // Finalize MAC
                _hmac.TransformFinalBlock(Array.Empty <byte>(), 0, 0);

                var storedMac   = _blockMac;
                var computedMac = _hmac.Hash;

                _blockIndex++;
                _blockMac = null;
                _hmac.Dispose();
                _hmac = null;

                if (!Crypto.AreEqual(storedMac, computedMac))
                {
                    throw new InternalErrorException("Corrupted, block MAC doesn't match");
                }
            }

            return(toCopy);
        }
        /// <summary>
        ///     Reads one field.
        /// </summary>
        /// <returns></returns>
        private PasswordSafeRecordField ReadField()
        {
            var currentBlock = new byte[16];

            ReadBytes(currentBlock, "Generic Field");
            if (BufferCompare(currentBlock, _eofMarkerBytes))
            {
                return(null);
            }

            _decryptor.TransformBlock(currentBlock, 0, currentBlock.Length, currentBlock, 0);

            if (!BitConverter.IsLittleEndian)
            {
                throw new InvalidProgramException("We must be little endian!");
            }

            var fieldLength = BitConverter.ToInt32(currentBlock, 0);

            if (fieldLength < 0)
            {
                throw new InvalidDataException("Invalid field length");
            }

            var type       = currentBlock[4];
            var dataBuffer = new byte[fieldLength];

            // There's a maximum of 11 bytes left of data in the first block.
            var lengthToCopy = fieldLength > 11 ? 11 : fieldLength;

            Buffer.BlockCopy(currentBlock, 5, dataBuffer, 0, lengthToCopy);
            fieldLength -= lengthToCopy;

            var currentOffset = lengthToCopy;

            // Now we've handled the first block. Let's see if there's more....
            if (fieldLength > 0)
            {
                var numberOfBlocks = (fieldLength - 1) / 16 + 1;
                while (numberOfBlocks-- > 0)
                {
                    ReadBytes(currentBlock, "Generic Field");
                    if (BufferCompare(currentBlock, _eofMarkerBytes))
                    {
                        throw new InvalidDataException("Unexpected end of file marker");
                        ;
                    }

                    _decryptor.TransformBlock(currentBlock, 0, currentBlock.Length, currentBlock, 0);

                    lengthToCopy = currentBlock.Length > fieldLength ? fieldLength : currentBlock.Length;
                    Buffer.BlockCopy(currentBlock, 0, dataBuffer, currentOffset, lengthToCopy);
                    fieldLength   -= lengthToCopy;
                    currentOffset += lengthToCopy;
                }
            }

            var tmpNull = new byte[dataBuffer.Length];

            // We only HMAC the actual data - not the length and type. That *SUCKS*!!!
            _hmac.TransformBlock(dataBuffer, 0, dataBuffer.Length, tmpNull, 0);

            return(new PasswordSafeRecordField(type, dataBuffer));
        }
 public void TransformBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset)
 {
     _algo.TransformBlock(inputBuffer, inputOffset, inputCount, outputBuffer, outputOffset);
 }
Beispiel #18
0
        /// <summary>
        /// Returns the SHA256 HMAC of the given <paramref name="prefix"/>, the given <paramref name="request"/>'s
        /// body, and the given <paramref name="suffix"/> (in that order).
        /// </summary>
        /// <param name="request">The current <see cref="HttpRequest"/>.</param>
        /// <param name="secret">The key data used to initialize the <see cref="HMACSHA256"/>.</param>
        /// <param name="prefix">
        /// If non-<see langword="null"/> and non-empty, additional <c>byte</c>s to include in the hashed content
        /// before the <paramref name="request"/>'s body.
        /// </param>
        /// <param name="suffix">
        /// If non-<see langword="null"/> and non-empty, additional <c>byte</c>s to include in the hashed content
        /// after the <paramref name="request"/>'s body.
        /// </param>
        /// <returns>
        /// A <see cref="Task"/> that on completion provides a <see cref="byte"/> array containing the SHA256 HMAC of
        /// the <paramref name="prefix"/>, the <paramref name="request"/>'s body, and the <paramref name="suffix"/>
        /// (in that order).
        /// </returns>
        protected virtual async Task <byte[]> ComputeRequestBodySha256HashAsync(
            HttpRequest request,
            byte[] secret,
            byte[] prefix,
            byte[] suffix)
        {
            if (request == null)
            {
                throw new ArgumentNullException(nameof(request));
            }
            if (secret == null)
            {
                throw new ArgumentNullException(nameof(secret));
            }
            if (secret.Length == 0)
            {
                throw new ArgumentException(Resources.General_ArgumentCannotBeNullOrEmpty);
            }

            await WebHookHttpRequestUtilities.PrepareRequestBody(request);

            using (var hasher = new HMACSHA256(secret))
            {
                try
                {
                    if (prefix != null && prefix.Length > 0)
                    {
                        hasher.TransformBlock(
                            inputBuffer: prefix,
                            inputOffset: 0,
                            inputCount: prefix.Length,
                            outputBuffer: null,
                            outputOffset: 0);
                    }

                    // Split body into 4K chunks.
                    var buffer      = new byte[4096];
                    var inputStream = request.Body;
                    int bytesRead;
                    while ((bytesRead = await inputStream.ReadAsync(buffer, 0, buffer.Length)) > 0)
                    {
                        hasher.TransformBlock(
                            buffer,
                            inputOffset: 0,
                            inputCount: bytesRead,
                            outputBuffer: null,
                            outputOffset: 0);
                    }

                    if (suffix != null && suffix.Length > 0)
                    {
                        hasher.TransformBlock(
                            suffix,
                            inputOffset: 0,
                            inputCount: suffix.Length,
                            outputBuffer: null,
                            outputOffset: 0);
                    }

                    hasher.TransformFinalBlock(Array.Empty <byte>(), inputOffset: 0, inputCount: 0);

                    return(hasher.Hash);
                }
                finally
                {
                    // Reset Position because JsonInputFormatter et cetera always start from current position.
                    request.Body.Seek(0L, SeekOrigin.Begin);
                }
            }
        }
Beispiel #19
0
        /// <summary>
        /// Verifies that the signature header matches that of the actual body.
        /// </summary>
        protected virtual async Task <bool> VerifySignature(HttpRequestMessage request, string id)
        {
            // 1. Ensure configuration for this receiver and id exists.
            var secretKey = await GetReceiverConfig(request, Name, id, SecretMinLength, SecretMaxLength);

            // 2. Get the timestamp and expected signatures from the signature header. Header is a comma-separated set
            // of key=value pairs.
            var    signatureHeaderValue = GetRequestHeader(request, SignatureHeaderName);
            var    pairs              = signatureHeaderValue.SplitAndTrim(',');
            string timestamp          = null;
            var    expectedSignatures = new List <string>();

            foreach (var pair in pairs)
            {
                var keyValuePair = pair.SplitAndTrim('=');
                if (keyValuePair.Length != 2)
                {
                    var message = string.Format(
                        CultureInfo.CurrentCulture,
                        StripeReceiverResources.Receiver_InvalidHeaderFormat,
                        SignatureHeaderName);
                    request.GetConfiguration().DependencyResolver.GetLogger().Error(message);

                    var invalidHeader = request.CreateErrorResponse(HttpStatusCode.BadRequest, message);
                    throw new HttpResponseException(invalidHeader);
                }

                if (string.Equals(keyValuePair[0], SignatureKey, StringComparison.OrdinalIgnoreCase))
                {
                    expectedSignatures.Add(keyValuePair[1]);
                }
                else if (string.IsNullOrEmpty(timestamp) &&
                         string.Equals(keyValuePair[0], TimestampKey, StringComparison.OrdinalIgnoreCase))
                {
                    timestamp = keyValuePair[1];
                }
            }

            if (string.IsNullOrEmpty(timestamp) || expectedSignatures.Count == 0)
            {
                var message = string.Format(
                    CultureInfo.CurrentCulture,
                    StripeReceiverResources.Receiver_HeaderMissingValue,
                    SignatureHeaderName,
                    TimestampKey,
                    SignatureKey);
                request.GetConfiguration().DependencyResolver.GetLogger().Error(message);

                var invalidHeader = request.CreateErrorResponse(HttpStatusCode.BadRequest, message);
                throw new HttpResponseException(invalidHeader);
            }

            // 3. Get the actual hash of the timestamp and request body.
            var secret = Encoding.UTF8.GetBytes(secretKey);
            var prefix = Encoding.UTF8.GetBytes(timestamp + ".");

            byte[] actualHash;
            using (var hasher = new HMACSHA256(secret))
            {
                hasher.TransformBlock(
                    inputBuffer: prefix,
                    inputOffset: 0,
                    inputCount: prefix.Length,
                    outputBuffer: null,
                    outputOffset: 0);

                var data = await request.Content.ReadAsByteArrayAsync();

                actualHash = hasher.ComputeHash(data);
            }

            // 4. Verify that the actual hash matches one of the expected hashes.
            foreach (var expectedSignature in expectedSignatures)
            {
                // While this looks repetitious compared to hex-encoding actualHash (once), a single v1 entry in the
                // header is the normal case. Expect multiple signatures only when rolling secret keys.
                byte[] expectedHash;
                try
                {
                    expectedHash = EncodingUtilities.FromHex(expectedSignature);
                }
                catch (Exception ex)
                {
                    var message = string.Format(
                        CultureInfo.CurrentCulture,
                        StripeReceiverResources.Receiver_BadSignatureEncoding,
                        SignatureHeaderName,
                        SignatureKey);
                    request.GetConfiguration().DependencyResolver.GetLogger().Error(message, ex);

                    var invalidEncoding = request.CreateErrorResponse(HttpStatusCode.BadRequest, message);
                    throw new HttpResponseException(invalidEncoding);
                }

                if (SecretEqual(expectedHash, actualHash))
                {
                    return(true);
                }
            }

            return(false);
        }
Beispiel #20
0
        /// <summary>
        /// Encryption Algorithm
        /// cell_iv = HMAC_SHA-2-256(iv_key, cell_data) truncated to 128 bits
        /// cell_ciphertext = AES-CBC-256(enc_key, cell_iv, cell_data) with PKCS7 padding.
        /// (optional) cell_tag = HMAC_SHA-2-256(mac_key, versionbyte + cell_iv + cell_ciphertext + versionbyte_length)
        /// cell_blob = versionbyte + [cell_tag] + cell_iv + cell_ciphertext
        /// </summary>
        /// <param name="plainText">Plaintext data to be encrypted</param>
        /// <param name="hasAuthenticationTag">Does the algorithm require authentication tag.</param>
        /// <returns>Returns the ciphertext corresponding to the plaintext.</returns>
        protected byte[] EncryptData(byte[] plainText, bool hasAuthenticationTag)
        {
            // Empty values get encrypted and decrypted properly for both Deterministic and Randomized encryptions.
            Debug.Assert(plainText != null);

            byte[] iv = new byte[BlockSizeInBytes];

            // Prepare IV
            // Should be 1 single block (16 bytes)
            if (this.isDeterministic)
            {
                SecurityUtility.GetHMACWithSHA256(plainText, this.dataEncryptionKey.IVKey, iv);
            }
            else
            {
                SecurityUtility.GenerateRandomBytes(iv);
            }

            int numBlocks = (plainText.Length / BlockSizeInBytes) + 1;

            // Final blob we return = version + HMAC + iv + cipherText
            const int hmacStartIndex       = 1;
            int       authenticationTagLen = hasAuthenticationTag ? KeySizeInBytes : 0;
            int       ivStartIndex         = hmacStartIndex + authenticationTagLen;
            int       cipherStartIndex     = ivStartIndex + BlockSizeInBytes; // this is where hmac starts.

            // Output buffer size = size of VersionByte + Authentication Tag + IV + cipher Text blocks.
            int outputBufSize = sizeof(byte) + authenticationTagLen + iv.Length + (numBlocks * BlockSizeInBytes);

            byte[] outBuffer = new byte[outputBufSize];

            // Store the version and IV rightaway
            outBuffer[0] = this.algorithmVersion;
            Buffer.BlockCopy(iv, 0, outBuffer, ivStartIndex, iv.Length);

            AesCryptoServiceProvider aesAlg;

            // Try to get a provider from the pool.
            // If no provider is available, create a new one.
            if (!this.cryptoProviderPool.TryDequeue(out aesAlg))
            {
                aesAlg = new AesCryptoServiceProvider();

                try
                {
                    // Set various algorithm properties
                    aesAlg.Key     = this.dataEncryptionKey.EncryptionKey;
                    aesAlg.Mode    = cipherMode;
                    aesAlg.Padding = paddingMode;
                }
                catch (Exception)
                {
                    if (aesAlg != null)
                    {
                        aesAlg.Dispose();
                    }

                    throw;
                }
            }

            try
            {
                // Always set the IV since it changes from cell to cell.
                aesAlg.IV = iv;

                // Compute CipherText and authentication tag in a single pass
                using (ICryptoTransform encryptor = aesAlg.CreateEncryptor())
                {
                    Debug.Assert(encryptor.CanTransformMultipleBlocks, "AES Encryptor can transform multiple blocks");
                    int count       = 0;
                    int cipherIndex = cipherStartIndex; // this is where cipherText starts
                    if (numBlocks > 1)
                    {
                        count        = (numBlocks - 1) * BlockSizeInBytes;
                        cipherIndex += encryptor.TransformBlock(plainText, 0, count, outBuffer, cipherIndex);
                    }

                    byte[] buffTmp = encryptor.TransformFinalBlock(plainText, count, plainText.Length - count); // done encrypting
                    Buffer.BlockCopy(buffTmp, 0, outBuffer, cipherIndex, buffTmp.Length);
                    cipherIndex += buffTmp.Length;
                }

                if (hasAuthenticationTag)
                {
                    using (HMACSHA256 hmac = new HMACSHA256(this.dataEncryptionKey.MACKey))
                    {
                        Debug.Assert(hmac.CanTransformMultipleBlocks, "HMAC can't transform multiple blocks");
                        hmac.TransformBlock(version, 0, version.Length, version, 0);
                        hmac.TransformBlock(iv, 0, iv.Length, iv, 0);

                        // Compute HMAC on final block
                        hmac.TransformBlock(outBuffer, cipherStartIndex, numBlocks * BlockSizeInBytes, outBuffer, cipherStartIndex);
                        hmac.TransformFinalBlock(versionSize, 0, versionSize.Length);
                        byte[] hash = hmac.Hash;
                        Debug.Assert(hash.Length >= authenticationTagLen, "Unexpected hash size");
                        Buffer.BlockCopy(hash, 0, outBuffer, hmacStartIndex, authenticationTagLen);
                    }
                }
            }
            finally
            {
                // Return the provider to the pool.
                this.cryptoProviderPool.Enqueue(aesAlg);
            }

            return(outBuffer);
        }
Beispiel #21
0
        /// <summary>
        /// Loads data from a file.
        /// If both passphrase and key are present, passphrase takes precedence.
        /// </summary>
        /// <param name="stream">Stream.</param>
        /// <param name="passphraseBuffer">Password bytes. Caller has to avoid keeping bytes unencrypted in memory.</param>
        /// <param name="keyBuffer">Key bytes containing both key K and L. Must be 64 bytes. Caller has to avoid keeping bytes unencrypted in memory.</param>
        /// <exception cref="ArgumentNullException">Stream cannot be null. -or- Passphrase cannot be null.</exception>
        /// <exception cref="ArgumentOutOfRangeException">Keys must be 64 bytes long.</exception>
        /// <exception cref="FormatException">Unrecognized file format. -or- Password mismatch. -or- Authentication mismatch.</exception>
        internal static Document InternalLoad(Stream stream, byte[]?passphraseBuffer, byte[]?keyBuffer)
        {
            if ((passphraseBuffer == null) && (keyBuffer == null))
            {
                throw new ArgumentNullException(nameof(passphraseBuffer), "Passphrase cannot be null.");
            }
            if ((passphraseBuffer == null) && (keyBuffer != null) && (keyBuffer.Length != 64))
            {
                throw new ArgumentOutOfRangeException(nameof(keyBuffer), "Keys must be 64 bytes long.");
            }

            var buffer = new byte[16384];

            using (var ms = new MemoryStream()) {
                int read;
                while ((read = stream.Read(buffer, 0, buffer.Length)) > 0)
                {
                    ms.Write(buffer, 0, read);
                }
                buffer = ms.ToArray();
            }

            if ((buffer.Length < 200) ||
                (BitConverter.ToInt32(buffer, 0) != Tag) ||
                (BitConverter.ToInt32(buffer, buffer.Length - 32 - 16) != Tag) ||
                (BitConverter.ToInt32(buffer, buffer.Length - 32 - 12) != TagEof) ||
                (BitConverter.ToInt32(buffer, buffer.Length - 32 - 8) != Tag) ||
                (BitConverter.ToInt32(buffer, buffer.Length - 32 - 4) != TagEof))
            {
                throw new FormatException("Unrecognized file format.");
            }

            var salt = new byte[32];

            Buffer.BlockCopy(buffer, 4, salt, 0, salt.Length);

            var iter = BitConverter.ToUInt32(buffer, 36);

            byte[]? stretchedKey = null, keyK = null, keyL = null, data = null;
            try {
                if (passphraseBuffer != null)
                {
                    stretchedKey = GetStretchedKey(passphraseBuffer, salt, iter);
                    if (!AreBytesTheSame(GetSha256Hash(stretchedKey), buffer, 40))
                    {
                        throw new CryptographicException("Password mismatch.");
                    }
                    keyK = DecryptKey(stretchedKey, buffer, 72);
                    keyL = DecryptKey(stretchedKey, buffer, 104);
                }
                else if (keyBuffer != null)
                {
                    keyK = new byte[32];
                    keyL = new byte[32];
                    Buffer.BlockCopy(keyBuffer, 0, keyK, 0, keyK.Length);
                    Buffer.BlockCopy(keyBuffer, 32, keyL, 0, keyL.Length);
                }
                else
                {
                    throw new InvalidOperationException("No passphrase or key buffer.");
                }

                var iv = new byte[16];
                Buffer.BlockCopy(buffer, 136, iv, 0, iv.Length);

                data = DecryptData(keyK, iv, buffer, 152, buffer.Length - 200);

                using var dataHash = new HMACSHA256(keyL);
                var dataOffset = 0;

                var headerFields = new List <Header>();
                while (dataOffset < data.Length)
                {
                    var fieldLength     = BitConverter.ToInt32(data, dataOffset + 0);
                    var fieldLengthFull = ((fieldLength + 5 - 1) / 16 + 1) * 16;
                    var fieldType       = (HeaderType)data[dataOffset + 4];
                    var fieldData       = new byte[fieldLength];
                    try {
                        Buffer.BlockCopy(data, dataOffset + 5, fieldData, 0, fieldLength);
                        dataOffset += fieldLengthFull;                                    //there is ALWAYS some random bytes added, thus extra block if 16 bytes

                        dataHash.TransformBlock(fieldData, 0, fieldData.Length, null, 0); //not hashing length nor type - wtf?
                        if (fieldType == HeaderType.EndOfEntry)
                        {
                            break;
                        }

                        headerFields.Add(new Header(fieldType, fieldData));
                    } finally {
                        Array.Clear(fieldData, 0, fieldData.Length);
                    }
                }

                if ((headerFields.Count == 0) || (headerFields[0].Version < 0x0300))
                {
                    throw new FormatException("Unrecognized file format version.");
                }

                var           recordFields = new List <List <Record> >();
                List <Record>?records      = null;
                while (dataOffset < data.Length)
                {
                    var fieldLength     = BitConverter.ToInt32(data, dataOffset + 0);
                    var fieldLengthFull = ((fieldLength + 5 - 1) / 16 + 1) * 16;
                    var fieldType       = (RecordType)data[dataOffset + 4];
                    var fieldData       = new byte[fieldLength];
                    try {
                        Buffer.BlockCopy(data, dataOffset + 5, fieldData, 0, fieldLength);
                        dataOffset += fieldLengthFull;                                    //there is ALWAYS some random bytes added, thus extra block if 16 bytes

                        dataHash.TransformBlock(fieldData, 0, fieldData.Length, null, 0); //not hashing length nor type - wtf?
                        if (fieldType == RecordType.EndOfEntry)
                        {
                            records = null; continue;
                        }

                        if (records == null)
                        {
                            records = new List <Record>();
                            recordFields.Add(records);
                        }
                        records.Add(new Record(fieldType, fieldData));
                    } finally {
                        Array.Clear(fieldData, 0, fieldData.Length);
                    }
                }

                dataHash.TransformFinalBlock(Array.Empty <byte>(), 0, 0);

                if (!AreBytesTheSame(dataHash.Hash, buffer, buffer.Length - 32))
                {
                    throw new CryptographicException("Authentication mismatch.");
                }

                return(new Document(passphraseBuffer, (int)iter, headerFields, recordFields.ToArray()));
            } catch (CryptographicException ex) {
                throw new FormatException(ex.Message, ex);
            } finally { //best effort to sanitize memory
                if (stretchedKey != null)
                {
                    Array.Clear(stretchedKey, 0, stretchedKey.Length);
                }
                if (keyK != null)
                {
                    Array.Clear(keyK, 0, keyK.Length);
                }
                if (keyL != null)
                {
                    Array.Clear(keyL, 0, keyL.Length);
                }
                if (data != null)
                {
                    Array.Clear(data, 0, data.Length);
                }
                if (buffer != null)
                {
                    Array.Clear(buffer, 0, buffer.Length);
                }
            }
        }