Exemplo n.º 1
0
        /// <summary>
        /// Writes out the encrypted KDBX inner header.
        /// </summary>
        /// <param name="writer">The DataWriter to write to.</param>
        /// <returns>A Task representing the asynchronous operation.</returns>
        private async Task WriteInnerHeaderAsync(DataWriter writer)
        {
            WriteFieldId(writer, InnerHeaderField.InnerRandomStreamID);
            WriteFieldSize(writer, 4);
            writer.WriteUInt32((uint)HeaderData.InnerRandomStream);
            await writer.StoreAsync();

            WriteFieldId(writer, InnerHeaderField.InnerRandomStreamKey);
            WriteFieldSize(writer, 32);
            writer.WriteBytes(HeaderData.InnerRandomStreamKey);
            await writer.StoreAsync();

            foreach (ProtectedBinary bin in HeaderData.ProtectedBinaries)
            {
                WriteFieldId(writer, InnerHeaderField.Binary);

                KdbxBinaryFlags flags = KdbxBinaryFlags.None;
                if (bin.ProtectionRequested)
                {
                    flags |= KdbxBinaryFlags.MemoryProtected;
                }
                byte[] data = bin.GetClearData();

                WriteFieldSize(writer, 1 + (uint)data.Length);
                writer.WriteByte((byte)flags);
                writer.WriteBytes(data);
                await writer.StoreAsync();
            }

            WriteFieldId(writer, InnerHeaderField.EndOfHeader);
            WriteFieldSize(writer, 0);
            await writer.StoreAsync();
        }
Exemplo n.º 2
0
        private void WriteInnerHeader(Stream s)
        {
            int nIrsID = (int)m_craInnerRandomStream;

            WriteInnerHeaderField(s, KdbxInnerHeaderFieldID.InnerRandomStreamID,
                                  MemUtil.Int32ToBytes(nIrsID), null);

            WriteInnerHeaderField(s, KdbxInnerHeaderFieldID.InnerRandomStreamKey,
                                  m_pbInnerRandomStreamKey, null);

            ProtectedBinary[] vBin = m_pbsBinaries.ToArray();
            for (int i = 0; i < vBin.Length; ++i)
            {
                ProtectedBinary pb = vBin[i];
                if (pb == null)
                {
                    throw new InvalidOperationException();
                }

                KdbxBinaryFlags f = KdbxBinaryFlags.None;
                if (pb.IsProtected)
                {
                    f |= KdbxBinaryFlags.Protected;
                }

                byte[] pbFlags = new byte[1] {
                    (byte)f
                };
                byte[] pbData = pb.ReadData();

                WriteInnerHeaderField(s, KdbxInnerHeaderFieldID.Binary,
                                      pbFlags, pbData);

                if (pb.IsProtected)
                {
                    MemUtil.ZeroByteArray(pbData);
                }
            }

            WriteInnerHeaderField(s, KdbxInnerHeaderFieldID.EndOfHeader,
                                  null, null);
        }
Exemplo n.º 3
0
        private bool ReadInnerHeaderField(BinaryReaderEx br)
        {
            Debug.Assert(br != null);
            if (br == null)
            {
                throw new ArgumentNullException("br");
            }

            byte btFieldID = br.ReadByte();

            int cbSize = MemUtil.BytesToInt32(br.ReadBytes(4));

            if (cbSize < 0)
            {
                throw new FormatException(KLRes.FileCorrupted);
            }

            byte[] pbData = MemUtil.EmptyByteArray;
            if (cbSize > 0)
            {
                pbData = br.ReadBytes(cbSize);
            }

            bool bResult = true;
            KdbxInnerHeaderFieldID kdbID = (KdbxInnerHeaderFieldID)btFieldID;

            switch (kdbID)
            {
            case KdbxInnerHeaderFieldID.EndOfHeader:
                bResult = false;                         // Returning false indicates end of header
                break;

            case KdbxInnerHeaderFieldID.InnerRandomStreamID:
                SetInnerRandomStreamID(pbData);
                break;

            case KdbxInnerHeaderFieldID.InnerRandomStreamKey:
                Debug.Assert(m_pbInnerRandomStreamKey == null);
                m_pbInnerRandomStreamKey = pbData;
                CryptoRandom.Instance.AddEntropy(pbData);
                break;

            case KdbxInnerHeaderFieldID.Binary:
                if (pbData.Length < 1)
                {
                    throw new FormatException();
                }
                KdbxBinaryFlags f     = (KdbxBinaryFlags)pbData[0];
                bool            bProt = ((f & KdbxBinaryFlags.Protected) != KdbxBinaryFlags.None);

                ProtectedBinary pb = new ProtectedBinary(bProt, pbData,
                                                         1, pbData.Length - 1);
                Debug.Assert(m_pbsBinaries.Find(pb) < 0);                         // No deduplication?
                m_pbsBinaries.Add(pb);

                if (bProt)
                {
                    MemUtil.ZeroByteArray(pbData);
                }
                break;

            default:
                Debug.Assert(false);
                break;
            }

            return(bResult);
        }
Exemplo n.º 4
0
        /// <summary>
        /// Attempts to read and validate the next header field in the data stream.
        /// </summary>
        /// <param name="reader">A reader over the document file.</param>
        /// <param name="headerData">The header data that has been extracted so far.</param>
        /// <returns>A Task representing the field that was read.</returns>
        private async Task <InnerHeaderField> ReadInnerHeaderField(DataReader reader, KdbxHeaderData headerData)
        {
            DebugHelper.Assert(this.parameters.UseInnerHeader);

            // A header is guaranteed to have 5 bytes at the beginning.
            // The first byte represents the type or ID of the header.
            // The next four bytes represent a 32-bit signed integer giving the size of the data field.
            await reader.LoadAsync(5U);

            // Read the header ID from the first byte
            InnerHeaderField fieldId = (InnerHeaderField)reader.ReadByte();

            // The cast above may have succeeded but still resulted in an unknown value (outside of the enum).
            // If so, we need to bail.
            if (!Enum.IsDefined(typeof(InnerHeaderField), fieldId))
            {
                throw new KdbxParseException(ReaderResult.FromHeaderFieldUnknown((byte)fieldId));
            }

            // Read the header data field size from the next two bytes
            uint size = reader.ReadUInt32();

            DebugHelper.Assert(size <= Int32.MaxValue, "Size should be an Int32");

            LoggingFields headerTraceFields = new LoggingFields();

            headerTraceFields.AddString("FieldId", fieldId.ToString());
            headerTraceFields.AddUInt32("Bytes", size);
            this.logger.LogEvent("KdbxReader.InnerHeaderField", headerTraceFields, EventVerbosity.Info);
            await reader.LoadAsync(size);

            byte[] data = new byte[size];
            reader.ReadBytes(data);

            // Based on the header field in question, the data is validated differently.
            // The size of the data field needs to be validated, and the data itself may need to be parsed.
            switch (fieldId)
            {
            case InnerHeaderField.EndOfHeader:
                break;

            case InnerHeaderField.InnerRandomStreamKey:
                RequireFieldDataSize(fieldId, size, (n) => n > 0, "must be nonzero");
                headerData.InnerRandomStreamKey = data;
                break;

            case InnerHeaderField.InnerRandomStreamID:
                RequireFieldDataSizeEq(fieldId, 4, size);
                headerData.InnerRandomStream = (RngAlgorithm)BitConverter.ToUInt32(data, 0);
                RequireEnumDefined(fieldId, headerData.InnerRandomStream);
                break;

            case InnerHeaderField.Binary:
                // Data := F || M where F is one byte and M is the binary content
                KdbxBinaryFlags flags = (KdbxBinaryFlags)data[0];
                ProtectedBinary bin   = new ProtectedBinary(
                    data,
                    1,
                    data.Length - 1,
                    flags.HasFlag(KdbxBinaryFlags.MemoryProtected)
                    );

                headerData.ProtectedBinaries.Add(bin);

                break;
            }

            return(fieldId);
        }