Ejemplo n.º 1
0
        // public void Save(string strFile, PwGroup pgDataSource, KdbxFormat fmt,
        //	IStatusLogger slLogger)
        // {
        //	bool bMadeUnhidden = UrlUtil.UnhideFile(strFile);
        //
        //	IOConnectionInfo ioc = IOConnectionInfo.FromPath(strFile);
        //	this.Save(IOConnection.OpenWrite(ioc), pgDataSource, format, slLogger);
        //
        //	if(bMadeUnhidden) UrlUtil.HideFile(strFile, true); // Hide again
        // }

        /// <summary>
        /// Save the contents of the current <c>PwDatabase</c> to a KDBX file.
        /// </summary>
        /// <param name="sSaveTo">Stream to write the KDBX file into.</param>
        /// <param name="pgDataSource">Group containing all groups and
        /// entries to write. If <c>null</c>, the complete database will
        /// be written.</param>
        /// <param name="fmt">Format of the file to create.</param>
        /// <param name="slLogger">Logger that recieves status information.</param>
        public void Save(Stream sSaveTo, PwGroup pgDataSource, KdbxFormat fmt,
                         IStatusLogger slLogger)
        {
            Debug.Assert(sSaveTo != null);
            if (sSaveTo == null)
            {
                throw new ArgumentNullException("sSaveTo");
            }

            if (m_bUsedOnce)
            {
                throw new InvalidOperationException("Do not reuse KdbxFile objects!");
            }
            m_bUsedOnce = true;

            m_format    = fmt;
            m_slLogger  = slLogger;
            m_xmlWriter = null;

            PwGroup      pgRoot   = (pgDataSource ?? m_pwDatabase.RootGroup);
            UTF8Encoding encNoBom = StrUtil.Utf8;
            CryptoRandom cr       = CryptoRandom.Instance;

            byte[] pbCipherKey = null;
            byte[] pbHmacKey64 = null;

            m_pbsBinaries.Clear();
            m_pbsBinaries.AddFrom(pgRoot);

            List <Stream> lStreams = new List <Stream>();

            lStreams.Add(sSaveTo);

            HashingStreamEx sHashing = new HashingStreamEx(sSaveTo, true, null);

            lStreams.Add(sHashing);

            try
            {
                m_uFileVersion = GetMinKdbxVersion();

                int           cbEncKey, cbEncIV;
                ICipherEngine iCipher = GetCipher(out cbEncKey, out cbEncIV);

                m_pbMasterSeed   = cr.GetRandomBytes(32);
                m_pbEncryptionIV = cr.GetRandomBytes((uint)cbEncIV);

                // m_pbTransformSeed = cr.GetRandomBytes(32);
                PwUuid    puKdf = m_pwDatabase.KdfParameters.KdfUuid;
                KdfEngine kdf   = KdfPool.Get(puKdf);
                if (kdf == null)
                {
                    throw new Exception(KLRes.UnknownKdf + MessageService.NewParagraph +
                                        // KLRes.FileNewVerOrPlgReq + MessageService.NewParagraph +
                                        "UUID: " + puKdf.ToHexString() + ".");
                }
                kdf.Randomize(m_pwDatabase.KdfParameters);

                if (m_format == KdbxFormat.Default)
                {
                    if (m_uFileVersion < FileVersion32_4)
                    {
                        m_craInnerRandomStream   = CrsAlgorithm.Salsa20;
                        m_pbInnerRandomStreamKey = cr.GetRandomBytes(32);
                    }
                    else                     // KDBX >= 4
                    {
                        m_craInnerRandomStream   = CrsAlgorithm.ChaCha20;
                        m_pbInnerRandomStreamKey = cr.GetRandomBytes(64);
                    }

                    m_randomStream = new CryptoRandomStream(m_craInnerRandomStream,
                                                            m_pbInnerRandomStreamKey);
                }

                if (m_uFileVersion < FileVersion32_4)
                {
                    m_pbStreamStartBytes = cr.GetRandomBytes(32);
                }

                Stream sXml;
                if (m_format == KdbxFormat.Default)
                {
                    byte[] pbHeader = GenerateHeader();
                    m_pbHashOfHeader = CryptoUtil.HashSha256(pbHeader);

                    MemUtil.Write(sHashing, pbHeader);
                    sHashing.Flush();

                    ComputeKeys(out pbCipherKey, cbEncKey, out pbHmacKey64);

                    Stream sPlain;
                    if (m_uFileVersion < FileVersion32_4)
                    {
                        Stream sEncrypted = EncryptStream(sHashing, iCipher,
                                                          pbCipherKey, cbEncIV, true);
                        if ((sEncrypted == null) || (sEncrypted == sHashing))
                        {
                            throw new SecurityException(KLRes.CryptoStreamFailed);
                        }
                        lStreams.Add(sEncrypted);

                        MemUtil.Write(sEncrypted, m_pbStreamStartBytes);

                        sPlain = new HashedBlockStream(sEncrypted, true);
                    }
                    else                     // KDBX >= 4
                    {
                        // For integrity checking (without knowing the master key)
                        MemUtil.Write(sHashing, m_pbHashOfHeader);

                        byte[] pbHeaderHmac = ComputeHeaderHmac(pbHeader, pbHmacKey64);
                        MemUtil.Write(sHashing, pbHeaderHmac);

                        Stream sBlocks = new HmacBlockStream(sHashing, true,
                                                             true, pbHmacKey64);
                        lStreams.Add(sBlocks);

                        sPlain = EncryptStream(sBlocks, iCipher, pbCipherKey,
                                               cbEncIV, true);
                        if ((sPlain == null) || (sPlain == sBlocks))
                        {
                            throw new SecurityException(KLRes.CryptoStreamFailed);
                        }
                    }
                    lStreams.Add(sPlain);

                    if (m_pwDatabase.Compression == PwCompressionAlgorithm.GZip)
                    {
                        sXml = new GZipStream(sPlain, CompressionMode.Compress);
                        lStreams.Add(sXml);
                    }
                    else
                    {
                        sXml = sPlain;
                    }

                    if (m_uFileVersion >= FileVersion32_4)
                    {
                        WriteInnerHeader(sXml);                         // Binary header before XML
                    }
                }
                else if (m_format == KdbxFormat.PlainXml)
                {
                    sXml = sHashing;
                }
                else
                {
                    Debug.Assert(false);
                    throw new ArgumentOutOfRangeException("fmt");
                }

                m_xmlWriter = XmlUtilEx.CreateXmlWriter(sXml);

                WriteDocument(pgRoot);

                m_xmlWriter.Flush();
            }
            finally
            {
                CommonCleanUpWrite(lStreams, sHashing);

                if (pbCipherKey != null)
                {
                    MemUtil.ZeroByteArray(pbCipherKey);
                }
                if (pbHmacKey64 != null)
                {
                    MemUtil.ZeroByteArray(pbHmacKey64);
                }
            }
        }
Ejemplo n.º 2
0
        // public void Save(string strFile, PwGroup pgDataSource, KdbxFormat format,
        //	IStatusLogger slLogger)
        // {
        //	bool bMadeUnhidden = UrlUtil.UnhideFile(strFile);
        //
        //	IOConnectionInfo ioc = IOConnectionInfo.FromPath(strFile);
        //	this.Save(IOConnection.OpenWrite(ioc), pgDataSource, format, slLogger);
        //
        //	if(bMadeUnhidden) UrlUtil.HideFile(strFile, true); // Hide again
        // }

        /// <summary>
        /// Save the contents of the current <c>PwDatabase</c> to a KDBX file.
        /// </summary>
        /// <param name="sSaveTo">Stream to write the KDBX file into.</param>
        /// <param name="pgDataSource">Group containing all groups and
        /// entries to write. If <c>null</c>, the complete database will
        /// be written.</param>
        /// <param name="format">Format of the file to create.</param>
        /// <param name="slLogger">Logger that recieves status information.</param>
        public void Save(Stream sSaveTo, PwGroup pgDataSource, KdbxFormat format,
                         IStatusLogger slLogger)
        {
            Debug.Assert(sSaveTo != null);
            if (sSaveTo == null)
            {
                throw new ArgumentNullException("sSaveTo");
            }

            m_format   = format;
            m_slLogger = slLogger;

            HashingStreamEx hashedStream = new HashingStreamEx(sSaveTo, true, null);

            UTF8Encoding encNoBom = StrUtil.Utf8;
            CryptoRandom cr       = CryptoRandom.Instance;

            try
            {
                m_pbMasterSeed    = cr.GetRandomBytes(32);
                m_pbTransformSeed = cr.GetRandomBytes(32);
                m_pbEncryptionIV  = cr.GetRandomBytes(16);

                m_pbProtectedStreamKey = cr.GetRandomBytes(32);
                m_craInnerRandomStream = CrsAlgorithm.Salsa20;
                m_randomStream         = new CryptoRandomStream(m_craInnerRandomStream,
                                                                m_pbProtectedStreamKey);

                m_pbStreamStartBytes = cr.GetRandomBytes(32);

                Stream writerStream;
                if (m_format == KdbxFormat.Default)
                {
                    WriteHeader(hashedStream);                     // Also flushes the stream

                    Stream sEncrypted = AttachStreamEncryptor(hashedStream);
                    if ((sEncrypted == null) || (sEncrypted == hashedStream))
                    {
                        throw new SecurityException(KLRes.CryptoStreamFailed);
                    }

                    sEncrypted.Write(m_pbStreamStartBytes, 0, m_pbStreamStartBytes.Length);

                    Stream sHashed = new HashedBlockStream(sEncrypted, true);

                    if (m_pwDatabase.Compression == PwCompressionAlgorithm.GZip)
                    {
                        writerStream = new GZipStream(sHashed, CompressionMode.Compress);
                    }
                    else
                    {
                        writerStream = sHashed;
                    }
                }
                else if (m_format == KdbxFormat.PlainXml)
                {
                    writerStream = hashedStream;
                }
                else
                {
                    Debug.Assert(false); throw new FormatException("KdbFormat");
                }

                m_xmlWriter = new XmlTextWriter(writerStream, encNoBom);
                WriteDocument(pgDataSource);

                m_xmlWriter.Flush();
                m_xmlWriter.Close();
                writerStream.Close();
            }
            finally { CommonCleanUpWrite(sSaveTo, hashedStream); }
        }
        // //////////////////////////////////////////////////////////////////
        // Import

        public override void Import(PwDatabase pwStorage, Stream sInput,
                                    IStatusLogger slLogger)
        {
            StreamReader sr         = new StreamReader(sInput, Encoding.UTF8);
            string       strContent = sr.ReadToEnd();

            sr.Close();

            if (strContent.IndexOf(@"<!DOCTYPE NETSCAPE-Bookmark-file-1>") < 0)
            {
                throw new FormatException("Invalid DOCTYPE!");
            }

            strContent = strContent.Replace(@"<!DOCTYPE NETSCAPE-Bookmark-file-1>", string.Empty);
            strContent = strContent.Replace(@"<HR>", string.Empty);
            strContent = strContent.Replace(@"<p>", string.Empty);
            // strContent = strContent.Replace(@"<DD>", string.Empty);
            // strContent = strContent.Replace(@"<DL>", string.Empty);
            // strContent = strContent.Replace(@"</DL>", string.Empty);
            strContent = strContent.Replace(@"<DT>", string.Empty);

            // int nOffset = strContent.IndexOf('&');
            // while(nOffset >= 0)
            // {
            //	string str4 = strContent.Substring(nOffset, 4);
            //	string str5 = strContent.Substring(nOffset, 5);
            //	string str6 = strContent.Substring(nOffset, 6);
            //	if((str6 != @"&nbsp;") && (str5 != @"&amp;") && (str4 != @"&lt;") &&
            //		(str4 != @"&gt;") && (str5 != @"&#39;") && (str6 != @"&quot;"))
            //	{
            //		strContent = strContent.Remove(nOffset, 1);
            //		strContent = strContent.Insert(nOffset, @"&amp;");
            //	}
            //	else nOffset = strContent.IndexOf('&', nOffset + 1);
            // }

            string[] vPreserve = new string[] { @"&nbsp;", @"&amp;", @"&lt;",
                                                @"&gt;", @"&#39;", @"&quot;" };
            Dictionary <string, string> dPreserve = new Dictionary <string, string>();
            CryptoRandom cr = CryptoRandom.Instance;

            foreach (string strPreserve in vPreserve)
            {
                string strCode = Convert.ToBase64String(cr.GetRandomBytes(16));
                Debug.Assert(strCode.IndexOf('&') < 0);
                dPreserve[strPreserve] = strCode;

                strContent = strContent.Replace(strPreserve, strCode);
            }
            strContent = strContent.Replace(@"&", @"&amp;");
            foreach (KeyValuePair <string, string> kvpPreserve in dPreserve)
            {
                strContent = strContent.Replace(kvpPreserve.Value, kvpPreserve.Key);
            }

            // Terminate <DD>s
            int iDD = -1;

            while (true)
            {
                iDD = strContent.IndexOf(@"<DD>", iDD + 1);
                if (iDD < 0)
                {
                    break;
                }

                int iNextTag = strContent.IndexOf('<', iDD + 1);
                if (iNextTag <= 0)
                {
                    Debug.Assert(false); break;
                }

                strContent = strContent.Insert(iNextTag, @"</DD>");
            }

            strContent = "<RootSentinel>" + strContent + "</META></RootSentinel>";

            byte[]       pbFixedData = StrUtil.Utf8.GetBytes(strContent);
            MemoryStream msFixed     = new MemoryStream(pbFixedData, false);

            XmlDocument xmlDoc = new XmlDocument();

            xmlDoc.Load(msFixed);
            msFixed.Close();

            XmlNode xmlRoot = xmlDoc.DocumentElement;

            foreach (XmlNode xmlChild in xmlRoot)
            {
                if (xmlChild.Name == "META")
                {
                    ImportMeta(xmlChild, pwStorage);
                }
            }
        }