private void CommonCleanUpRead(Stream sSource, HashingStreamEx hashedStream)
        {
            hashedStream.Close();
            m_pbHashOfFileOnDisk = hashedStream.Hash;

            sSource.Close();
        }
Beispiel #2
0
        public void LoadHeader(Stream sSource, KdbxFormat kdbFormat)
        {
            Debug.Assert(sSource != null);
            if (sSource == null)
            {
                throw new ArgumentNullException("sSource");
            }

            this.m_format = kdbFormat;

            var hashedStream = new HashingStreamEx(sSource, false, null);

            var encNoBom = StrUtil.Utf8;

            try
            {
                if (kdbFormat != KdbxFormat.Default)
                {
                    return;
                }

                var br = new BinaryReaderEx(hashedStream, encNoBom, KLRes.FileCorrupted);
                this.ReadHeader(br);
            }
            catch (Exception)
            {
                throw;
            }
            finally
            {
                this.CommonCleanUpRead(sSource, hashedStream);
            }
        }
Beispiel #3
0
        private void CommonCleanUpRead(Stream sSource, HashingStreamEx hashedStream)
        {
            hashedStream.Close();
            m_pbHashOfFileOnDisk = hashedStream.Hash;

            sSource.Close();

            // Reset memory protection settings (to always use reasonable
            // defaults)
            m_pwDatabase.MemoryProtection = new MemoryProtectionConfig();

            // Remove old backups (this call is required here in order to apply
            // the default history maintenance settings for people upgrading from
            // KeePass <= 2.14 to >= 2.15; also it ensures history integrity in
            // case a different application has created the KDBX file and ignored
            // the history maintenance settings)
            m_pwDatabase.MaintainBackups();             // Don't mark database as modified

            // Expand the root group, such that in case the user accidently
            // collapses the root group he can simply reopen the database
            PwGroup pgRoot = m_pwDatabase.RootGroup;

            if (pgRoot != null)
            {
                pgRoot.IsExpanded = true;
            }
            else
            {
                Debug.Assert(false);
            }

            m_pbHashOfHeader = null;
        }
Beispiel #4
0
        public void TestRead()
        {
            // if we use larger size, StreamReader will read past newline and cause bad hash
            var bytes = new byte[data.Length + 1];

            using (var ms = new MemoryStream(bytes))
            {
                using (var sw = new StreamWriter(ms))
                {
                    // set NewLine to ensure we don't run into cross-platform issues on Windows
                    sw.NewLine = "\n";
                    sw.WriteLine(data);
                }
            }

            using (var ms = new MemoryStream(bytes))
            {
                using (var hs = new HashingStreamEx(ms, false, null))
                {
                    using (var sr = new StreamReader(hs))
                    {
                        var read = sr.ReadLine();
                        Assert.That(read, Is.EqualTo(data));
                    }

                    // When the StreamReader is disposed, it calls Dispose on the
                    //HasingStreamEx, which computes the hash.
                    Assert.That(hs.Hash, Is.EqualTo(sha256HashOfData));
                }
            }
        }
Beispiel #5
0
        public void TestWrite()
        {
            var bytes = new byte[16];

            using (var ms = new MemoryStream(bytes))
            {
                using (var hs = new HashingStreamEx(ms, true, null))
                {
                    using (var sw = new StreamWriter(hs))
                    {
                        // set NewLine to ensure we don't run into cross-platform issues on Windows
                        sw.NewLine = "\n";
                        sw.WriteLine(data);
                    }

                    // When the StreamWriter is disposed, it calls Dispose on the
                    //HasingStreamEx, which computes the hash.
                    Assert.That(hs.Hash, Is.EqualTo(sha256HashOfData));
                }
            }

            using (var ms = new MemoryStream(bytes))
            {
                using (var sr = new StreamReader(ms))
                {
                    var read = sr.ReadLine();
                    Assert.That(read, Is.EqualTo(data));
                }
            }
        }
        internal void CommonCleanUpWrite(Stream sSaveTo, HashingStreamEx hashedStream)
        {
            hashedStream.Close();
            m_pbHashOfFileOnDisk = hashedStream.Hash;

            sSaveTo.Close();

            m_xmlWriter = null;
        }
Beispiel #7
0
        private void CommonCleanUpWrite(Stream sSaveTo, HashingStreamEx hashedStream)
        {
            hashedStream.Dispose();
            m_pbHashOfFileOnDisk = hashedStream.Hash;

            sSaveTo.Dispose();

            m_xmlWriter      = null;
            m_pbHashOfHeader = null;
        }
        private void CommonCleanUpWrite(List <Stream> lStreams, HashingStreamEx sHashing)
        {
            CloseStreams(lStreams);

            Debug.Assert(lStreams.Contains(sHashing));             // sHashing must be closed
            m_pbHashOfFileOnDisk = sHashing.Hash;
            Debug.Assert(m_pbHashOfFileOnDisk != null);

            CleanUpInnerRandomStream();

            m_xmlWriter      = null;
            m_pbHashOfHeader = null;
        }
Beispiel #9
0
        private static string HashStream(Stream sourceStream, Stream destStream)
        {
            string hash;

            using (var hashingRemoteStream = new HashingStreamEx(sourceStream, false, new SHA256Managed()))
            {
                hashingRemoteStream.CopyTo(destStream);
                hashingRemoteStream.Close();
                hash = MemUtil.ByteArrayToHexString(hashingRemoteStream.Hash);
                destStream.Position = 0;
            }

            return(hash);
        }
Beispiel #10
0
        private void CommonCleanUpRead(Stream sSource, HashingStreamEx hashedStream)
        {
            hashedStream.Close();
            m_pbHashOfFileOnDisk = hashedStream.Hash;

            sSource.Close();

            // Reset memory protection settings (to always use reasonable
            // defaults)
            m_pwDatabase.MemoryProtection = new MemoryProtectionConfig();

            // Remove old backups (this call is required here in order to apply
            // the default history maintenance settings for people upgrading from
            // KeePass <= 2.14 to >= 2.15; also it ensures history integrity in
            // case a different application has created the KDBX file and ignored
            // the history maintenance settings)
            m_pwDatabase.MaintainBackups();             // Don't mark database as modified
        }
        public MemoryStream GetRemoteDataAndHash(IOConnectionInfo ioc, out string hash)
        {
            MemoryStream remoteData = new MemoryStream();

            using (var remoteStream = _cachedStorage.OpenFileForRead(ioc))
            {
                //note: directly copying to remoteData and hashing causes NullReferenceExceptions in FTP and with Digest auth
                // -> use the temp data approach:
                MemoryStream tempData = new MemoryStream();
                remoteStream.CopyTo(tempData);
                tempData.Position = 0;
                HashingStreamEx hashingRemoteStream = new HashingStreamEx(tempData, false, new SHA256Managed());

                hashingRemoteStream.CopyTo(remoteData);
                hashingRemoteStream.Close();
                hash = MemUtil.ByteArrayToHexString(hashingRemoteStream.Hash);
            }
            remoteData.Position = 0;
            return(remoteData);
        }
Beispiel #12
0
        public override void Run()
        {
            try
            {
                IOConnectionInfo ioc         = _app.GetDb().Ioc;
                IFileStorage     fileStorage = _app.GetFileStorage(ioc);
                if (fileStorage is CachingFileStorage)
                {
                    throw new Exception("Cannot sync a cached database!");
                }
                StatusLogger.UpdateMessage(UiStringKey.CheckingDatabaseForChanges);

                //download file from remote location and calculate hash:
                StatusLogger.UpdateSubMessage(_app.GetResourceString(UiStringKey.DownloadingRemoteFile));


                MemoryStream remoteData = new MemoryStream();
                using (
                    HashingStreamEx hashingRemoteStream = new HashingStreamEx(fileStorage.OpenFileForRead(ioc), false,
                                                                              new SHA256Managed()))
                {
                    hashingRemoteStream.CopyTo(remoteData);
                    hashingRemoteStream.Close();

                    if (!MemUtil.ArraysEqual(_app.GetDb().KpDatabase.HashOfFileOnDisk, hashingRemoteStream.Hash))
                    {
                        _app.TriggerReload(_context);
                        Finish(true);
                    }
                    else
                    {
                        Finish(true, _app.GetResourceString(UiStringKey.RemoteDatabaseUnchanged));
                    }
                }
            }
            catch (Exception e)
            {
                Finish(false, e.Message);
            }
        }
Beispiel #13
0
        private void CommonCleanUpWrite(List <Stream> lStreams, HashingStreamEx sHashing)
        {
            if (m_xmlWriter != null)
            {
#if KeePassUWP
                m_xmlWriter.Dispose();
#else
                m_xmlWriter.Close();
#endif
                m_xmlWriter = null;
            }

            CloseStreams(lStreams);

            Debug.Assert(lStreams.Contains(sHashing));             // sHashing must be closed
            m_pbHashOfFileOnDisk = sHashing.Hash;
            Debug.Assert(m_pbHashOfFileOnDisk != null);

            CleanUpInnerRandomStream();

            m_pbHashOfHeader = null;
        }
        /// <summary>
        /// copies the file in ioc to the local cache. Updates the cache version files and returns the new file hash.
        /// </summary>
        protected string UpdateCacheFromRemote(IOConnectionInfo ioc, string cachedFilePath)
        {
            //note: we might use the file version to check if it's already in the cache and if copying is required.
            //However, this is safer.
            string fileHash;

            //open stream:
            using (Stream remoteFile = _cachedStorage.OpenFileForRead(ioc))
            {
                using (HashingStreamEx cachedFile = new HashingStreamEx(File.Create(cachedFilePath), true, new SHA256Managed()))
                {
                    remoteFile.CopyTo(cachedFile);
                    cachedFile.Close();
                    fileHash = MemUtil.ByteArrayToHexString(cachedFile.Hash);
                }
            }

            //save hash in cache files:
            File.WriteAllText(VersionFilePath(ioc), fileHash);
            File.WriteAllText(BaseVersionFilePath(ioc), fileHash);
            return(fileHash);
        }
Beispiel #15
0
            public void CommitWrite()
            {
                _committed = true;
                _memoryStream.Close();

                //write file to cache:
                //(note: this might overwrite local changes. It's assumed that a sync operation or check was performed before

                byte[] output = _memoryStream.ToArray();

                string hash;

                using (var hashingStream = new HashingStreamEx(File.Create(_cachingFileStorage.CachedFilePath(_ioc)), true, new SHA256Managed()))
                {
                    hashingStream.Write(output, 0, output.Length);

                    hashingStream.Close();
                    hash = MemUtil.ByteArrayToHexString(hashingStream.Hash);
                }

                File.WriteAllText(_cachingFileStorage.VersionFilePath(_ioc), hash);
                //create another memory stream which is open for reading again
                MemoryStream openMemStream = new MemoryStream(output);

                //update file on remote. This might overwrite changes there as well, see above.
                if (_cachingFileStorage.IsCached(_ioc))
                {
                    //if the file already is in the cache, it's ok if writing to remote fails.
                    _cachingFileStorage.TryUpdateRemoteFile(openMemStream, _ioc, _useFileTransaction, hash);
                }
                else
                {
                    //if not, we don't accept a failure (e.g. invalid credentials would always remain a problem)
                    _cachingFileStorage.UpdateRemoteFile(openMemStream, _ioc, _useFileTransaction, hash);
                }

                openMemStream.Dispose();
            }
                public override void Close()
                {
                    if (_closed)
                    {
                        return;
                    }

                    //write file to cache:
                    //(note: this might overwrite local changes. It's assumed that a sync operation or check was performed before
                    string hash;

                    using (var hashingStream = new HashingStreamEx(File.Create(_cachingFileStorage.CachedFilePath(ioc)), true, new SHA256Managed()))
                    {
                        Position = 0;
                        CopyTo(hashingStream);

                        hashingStream.Close();
                        hash = MemUtil.ByteArrayToHexString(hashingStream.Hash);
                    }

                    File.WriteAllText(_cachingFileStorage.VersionFilePath(ioc), hash);
                    //update file on remote. This might overwrite changes there as well, see above.
                    Position = 0;
                    if (_cachingFileStorage.IsCached(ioc))
                    {
                        //if the file already is in the cache, it's ok if writing to remote fails.
                        _cachingFileStorage.TryUpdateRemoteFile(this, ioc, _useFileTransaction, hash);
                    }
                    else
                    {
                        //if not, we don't accept a failure (e.g. invalid credentials would always remain a problem)
                        _cachingFileStorage.UpdateRemoteFile(this, ioc, _useFileTransaction, hash);
                    }

                    base.Close();

                    _closed = true;
                }
Beispiel #17
0
        public void PopulateDatabaseFromStream(PwDatabase db, Stream s, IStatusLogger slLogger)
        {
                        #if !EXCLUDE_KEYTRANSFORM
            var importer = new Com.Keepassdroid.Database.Load.ImporterV3();

            var hashingStream = new HashingStreamEx(s, false, new SHA256Managed());

            _metaStreams = new List <PwEntryV3>();

            string      password    = "";    //no need to distinguish between null and "" because empty passwords are invalid (and null is not allowed)
            KcpPassword passwordKey = (KcpPassword)db.MasterKey.GetUserKey(typeof(KcpPassword));
            if (passwordKey != null)
            {
                password = passwordKey.Password.ReadString();
            }

            KcpKeyFile   passwordKeyfile = (KcpKeyFile)db.MasterKey.GetUserKey(typeof(KcpKeyFile));
            MemoryStream keyfileStream   = null;
            if (passwordKeyfile != null)
            {
                keyfileStream = new MemoryStream(passwordKeyfile.RawFileData.ReadData());
            }


            try
            {
                var dbv3 = importer.OpenDatabase(hashingStream, password, keyfileStream);

                db.Name          = dbv3.Name;
                db.KdfParameters = (new AesKdf()).GetDefaultParameters();
                db.KdfParameters.SetUInt64(AesKdf.ParamRounds, (ulong)dbv3.NumKeyEncRounds);


                db.RootGroup = ConvertGroup(dbv3.RootGroup);
                if (dbv3.Algorithm == PwEncryptionAlgorithm.Rjindal)
                {
                    db.DataCipherUuid = StandardAesEngine.AesUuid;
                }
                else
                {
                    db.DataCipherUuid = new PwUuid(TwofishCipher.TwofishCipherEngine.TwofishCipherUuidBytes);
                }
            }
            catch (Java.IO.FileNotFoundException e)
            {
                throw new FileNotFoundException(
                          e.Message, e);
            }
            catch (Java.Lang.Exception e)
            {
                if (e.Message == "Invalid key!")
                {
                    throw new InvalidCompositeKeyException();
                }
                throw new Exception(e.LocalizedMessage ??
                                    e.Message ??
                                    e.GetType().Name, e);
            }

            HashOfLastStream = hashingStream.Hash;
            if (HashOfLastStream == null)
            {
                throw new Exception("hashing didn't work");                 //todo remove
            }
#else
            throw new Exception("Kdb is excluded with Key transform!");
#endif
        }
Beispiel #18
0
        /// <summary>
        /// Load a KDB file from a stream.
        /// </summary>
        /// <param name="sSource">Stream to read the data from. Must contain
        /// a KDBX stream.</param>
        /// <param name="kdbFormat">Format specifier.</param>
        /// <param name="slLogger">Status logger (optional).</param>
        public void Load(Stream sSource, KdbxFormat kdbFormat, IStatusLogger slLogger)
        {
            Debug.Assert(sSource != null);
            if (sSource == null)
            {
                throw new ArgumentNullException("sSource");
            }

            m_format   = kdbFormat;
            m_slLogger = slLogger;

            HashingStreamEx hashedStream = new HashingStreamEx(sSource, false, null);

            UTF8Encoding encNoBom = StrUtil.Utf8;

            try
            {
                BinaryReaderEx br           = null;
                BinaryReaderEx brDecrypted  = null;
                Stream         readerStream = null;

                if (kdbFormat == KdbxFormat.Default)
                {
                    br = new BinaryReaderEx(hashedStream, encNoBom, KLRes.FileCorrupted);
                    ReadHeader(br);

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

                    brDecrypted = new BinaryReaderEx(sDecrypted, encNoBom, KLRes.FileCorrupted);
                    byte[] pbStoredStartBytes = brDecrypted.ReadBytes(32);

                    if ((m_pbStreamStartBytes == null) || (m_pbStreamStartBytes.Length != 32))
                    {
                        throw new InvalidDataException();
                    }

                    for (int iStart = 0; iStart < 32; ++iStart)
                    {
                        if (pbStoredStartBytes[iStart] != m_pbStreamStartBytes[iStart])
                        {
                            throw new InvalidCompositeKeyException();
                        }
                    }

                    Stream sHashed = new HashedBlockStream(sDecrypted, false, 0,
                                                           !m_bRepairMode);

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

                if (kdbFormat != KdbxFormat.PlainXml)                // Is an encrypted format
                {
                    if (m_pbProtectedStreamKey == null)
                    {
                        Debug.Assert(false);
                        throw new SecurityException("Invalid protected stream key!");
                    }

                    m_randomStream = new CryptoRandomStream(m_craInnerRandomStream,
                                                            m_pbProtectedStreamKey);
                }
                else
                {
                    m_randomStream = null;                  // No random stream for plain-text files
                }
#if KeePassDebug_WriteXml
                // FileStream fsOut = new FileStream("Raw.xml", FileMode.Create,
                //	FileAccess.Write, FileShare.None);
                // try
                // {
                //	while(true)
                //	{
                //		int b = readerStream.ReadByte();
                //		if(b == -1) break;
                //		fsOut.WriteByte((byte)b);
                //	}
                // }
                // catch(Exception) { }
                // fsOut.Close();
#endif

                ReadXmlStreamed(readerStream, hashedStream);
                // ReadXmlDom(readerStream);

                readerStream.Dispose();
                // GC.KeepAlive(br);
                // GC.KeepAlive(brDecrypted);
            }
#if !KeePass2PCL
            catch (CryptographicException)            // Thrown on invalid padding
            {
                throw new CryptographicException(KLRes.FileCorrupted);
            }
#endif
            finally { CommonCleanUpRead(sSource, hashedStream); }
        }
        // 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 = new ProtectedBinarySet(true);
            m_pbsBinaries.AddFrom(pgRoot);

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

            lStreams.Add(sSaveTo);

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

            lStreams.Add(sHashing);

            try
            {
                // Fix history entries (should not be necessary; just for safety,
                // as e.g. XPath searches depend on correct history entry UUIDs)
                if (m_pwDatabase.MaintainBackups())
                {
                    Debug.Assert(false);
                }

                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 || m_format == KdbxFormat.ProtocolBuffers)
                {
                    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");
                }

                var stopWatch = Stopwatch.StartNew();

                if (m_format == KdbxFormat.ProtocolBuffers)
                {
                    KdbpFile.WriteDocument(m_pwDatabase, sXml, m_pbInnerRandomStreamKey, m_pbHashOfHeader);
                }
                else
                {
#if KeePassUAP
                    XmlWriterSettings xws = new XmlWriterSettings();
                    xws.Encoding            = encNoBom;
                    xws.Indent              = true;
                    xws.IndentChars         = "\t";
                    xws.NewLineOnAttributes = false;

                    XmlWriter xw = XmlWriter.Create(sXml, xws);
#else
                    XmlTextWriter xw = new XmlTextWriter(sXml, encNoBom);

                    xw.Formatting  = Formatting.Indented;
                    xw.IndentChar  = '\t';
                    xw.Indentation = 1;
#endif
                    m_xmlWriter = xw;

                    WriteDocument(pgRoot);

                    m_xmlWriter.Flush();
                    m_xmlWriter.Close();
                }
                Kp2aLog.Log(String.Format("{1}: {0}ms", stopWatch.ElapsedMilliseconds, m_format == KdbxFormat.ProtocolBuffers ? "KdbpFile.WriteDocument" : "Xml WriteDocument"));
            }
            finally
            {
                if (pbCipherKey != null)
                {
                    MemUtil.ZeroByteArray(pbCipherKey);
                }
                if (pbHmacKey64 != null)
                {
                    MemUtil.ZeroByteArray(pbHmacKey64);
                }

                CommonCleanUpWrite(lStreams, sHashing);
            }
        }
        /// <summary>
        /// Load a KDB file from a stream.
        /// </summary>
        /// <param name="sSource">Stream to read the data from. Must contain
        /// a KDB4 stream.</param>
        /// <param name="kdbFormat">Format specifier.</param>
        /// <param name="slLogger">Status logger (optional).</param>
        public void Load(Stream sSource, Kdb4Format kdbFormat, IStatusLogger slLogger)
        {
            Debug.Assert(sSource != null);
            if (sSource == null)
            {
                throw new ArgumentNullException("sSource");
            }

            m_format   = kdbFormat;
            m_slLogger = slLogger;

            HashingStreamEx hashedStream = new HashingStreamEx(sSource, false, null);

            UTF8Encoding encNoBom = new UTF8Encoding(false, false);

            try
            {
                BinaryReaderEx br           = null;
                BinaryReaderEx brDecrypted  = null;
                Stream         readerStream = null;

                if (kdbFormat == Kdb4Format.Default)
                {
                    br = new BinaryReaderEx(hashedStream, encNoBom, KLRes.FileCorrupted);
                    ReadHeader(br);

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

                    brDecrypted = new BinaryReaderEx(sDecrypted, encNoBom, KLRes.FileCorrupted);
                    byte[] pbStoredStartBytes = brDecrypted.ReadBytes(32);

                    if ((m_pbStreamStartBytes == null) || (m_pbStreamStartBytes.Length != 32))
                    {
                        throw new InvalidDataException();
                    }

                    for (int iStart = 0; iStart < 32; ++iStart)
                    {
                        if (pbStoredStartBytes[iStart] != m_pbStreamStartBytes[iStart])
                        {
                            throw new InvalidCompositeKeyException();
                        }
                    }

                    Stream sHashed = new HashedBlockStream(sDecrypted, false, 0,
                                                           !m_bRepairMode);

                    if (m_pwDatabase.Compression == PwCompressionAlgorithm.GZip)
                    {
                        readerStream = new GZipStream(sHashed, CompressionMode.Decompress);
                    }
                    else
                    {
                        readerStream = sHashed;
                    }
                }
                else if (kdbFormat == Kdb4Format.PlainXml)
                {
                    readerStream = hashedStream;
                }
                else
                {
                    Debug.Assert(false); throw new FormatException("KdbFormat");
                }

                if (kdbFormat != Kdb4Format.PlainXml)                // Is an encrypted format
                {
                    if (m_pbProtectedStreamKey == null)
                    {
                        Debug.Assert(false);
                        throw new SecurityException("Invalid protected stream key!");
                    }

                    m_randomStream = new CryptoRandomStream(m_craInnerRandomStream,
                                                            m_pbProtectedStreamKey);
                }
                else
                {
                    m_randomStream = null;                  // No random stream for plain text files
                }
                ReadXmlStreamed(readerStream, hashedStream);
                // ReadXmlDom(readerStream);

                GC.KeepAlive(brDecrypted);
                GC.KeepAlive(br);
            }
            catch (CryptographicException)            // Thrown on invalid padding
            {
                throw new CryptographicException(KLRes.FileCorrupted);
            }
            finally { CommonCleanUpRead(sSource, hashedStream); }
        }
Beispiel #21
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");
                }

#if KeePass2PCL
                var settings = new XmlWriterSettings()
                {
                    Encoding     = encNoBom,
                    Indent       = true,
                    IndentChars  = "\t",
                    NewLineChars = "\r\n",
                };
                m_xmlWriter = XmlWriter.Create(writerStream, settings);
#else
                m_xmlWriter = new XmlTextWriter(writerStream, encNoBom);
#endif
                WriteDocument(pgDataSource);

                m_xmlWriter.Flush();
                m_xmlWriter.Dispose();
                writerStream.Dispose();
            }
            finally { CommonCleanUpWrite(sSaveTo, hashedStream); }
        }
Beispiel #22
0
        /// <summary>
        /// Load a KDBX file from a stream.
        /// </summary>
        /// <param name="sSource">Stream to read the data from. Must contain
        /// a KDBX stream.</param>
        /// <param name="fmt">Format.</param>
        /// <param name="slLogger">Status logger (optional).</param>
        public void Load(Stream sSource, KdbxFormat fmt, IStatusLogger slLogger)
        {
            Debug.Assert(sSource != null);
            if (sSource == null)
            {
                throw new ArgumentNullException("sSource");
            }

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

#if KDBX_BENCHMARK
            Stopwatch swTime = Stopwatch.StartNew();
#endif

            m_format   = fmt;
            m_slLogger = slLogger;

            // Other applications might not perform a deduplication
            m_pbsBinaries = new ProtectedBinarySet(false);

            UTF8Encoding encNoBom    = StrUtil.Utf8;
            byte[]       pbCipherKey = null;
            byte[]       pbHmacKey64 = null;

            List <Stream> lStreams = new List <Stream>();
            lStreams.Add(sSource);

            HashingStreamEx sHashing = new HashingStreamEx(sSource, false, null);
            lStreams.Add(sHashing);

            try
            {
                Stream sXml;
                if (fmt == KdbxFormat.Default)
                {
                    BinaryReaderEx br = new BinaryReaderEx(sHashing,
                                                           encNoBom, KLRes.FileCorrupted);
                    byte[] pbHeader = LoadHeader(br);
                    m_pbHashOfHeader = CryptoUtil.HashSha256(pbHeader);

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

                    ComputeKeys(out pbCipherKey, cbEncKey, out pbHmacKey64);

                    string strIncomplete = KLRes.FileHeaderCorrupted + " " +
                                           KLRes.FileIncomplete;

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

                        BinaryReaderEx brDecrypted = new BinaryReaderEx(sDecrypted,
                                                                        encNoBom, strIncomplete);
                        byte[] pbStoredStartBytes = brDecrypted.ReadBytes(32);

                        if ((m_pbStreamStartBytes == null) || (m_pbStreamStartBytes.Length != 32))
                        {
                            throw new EndOfStreamException(strIncomplete);
                        }
                        if (!MemUtil.ArraysEqual(pbStoredStartBytes, m_pbStreamStartBytes))
                        {
                            throw new InvalidCompositeKeyException();
                        }

                        sPlain = new HashedBlockStream(sDecrypted, false, 0, !m_bRepairMode);
                    }
                    else                     // KDBX >= 4
                    {
                        byte[] pbStoredHash = MemUtil.Read(sHashing, 32);
                        if ((pbStoredHash == null) || (pbStoredHash.Length != 32))
                        {
                            throw new EndOfStreamException(strIncomplete);
                        }
                        if (!MemUtil.ArraysEqual(m_pbHashOfHeader, pbStoredHash))
                        {
                            throw new InvalidDataException(KLRes.FileHeaderCorrupted);
                        }

                        byte[] pbHeaderHmac = ComputeHeaderHmac(pbHeader, pbHmacKey64);
                        byte[] pbStoredHmac = MemUtil.Read(sHashing, 32);
                        if ((pbStoredHmac == null) || (pbStoredHmac.Length != 32))
                        {
                            throw new EndOfStreamException(strIncomplete);
                        }
                        if (!MemUtil.ArraysEqual(pbHeaderHmac, pbStoredHmac))
                        {
                            throw new InvalidCompositeKeyException();
                        }

                        HmacBlockStream sBlocks = new HmacBlockStream(sHashing,
                                                                      false, !m_bRepairMode, pbHmacKey64);
                        lStreams.Add(sBlocks);

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

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

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

                if (fmt == KdbxFormat.Default)
                {
                    if (m_pbInnerRandomStreamKey == null)
                    {
                        Debug.Assert(false);
                        throw new SecurityException("Invalid inner random stream key!");
                    }

                    m_randomStream = new CryptoRandomStream(m_craInnerRandomStream,
                                                            m_pbInnerRandomStreamKey);
                }

#if KeePassDebug_WriteXml
#warning XML output is enabled!

                /* using(FileStream fsOut = new FileStream("Raw.xml", FileMode.Create,
                 *      FileAccess.Write, FileShare.None))
                 * {
                 *      while(true)
                 *      {
                 *              int b = sXml.ReadByte();
                 *              if(b == -1) throw new EndOfStreamException();
                 *              fsOut.WriteByte((byte)b);
                 *      }
                 * } */
#endif

                ReadXmlStreamed(sXml, sHashing);
                // ReadXmlDom(sXml);
            }
            catch (CryptographicException)            // Thrown on invalid padding
            {
                throw new CryptographicException(KLRes.FileCorrupted);
            }
            finally
            {
                if (pbCipherKey != null)
                {
                    MemUtil.ZeroByteArray(pbCipherKey);
                }
                if (pbHmacKey64 != null)
                {
                    MemUtil.ZeroByteArray(pbHmacKey64);
                }

                CommonCleanUpRead(lStreams, sHashing);
            }

#if KDBX_BENCHMARK
            swTime.Stop();
            MessageService.ShowInfo("Loading KDBX took " +
                                    swTime.ElapsedMilliseconds.ToString() + " ms.");
#endif
        }
Beispiel #23
0
        public void Save(PwDatabase kpDatabase, Stream stream)
        {
            PwDatabaseV3 db       = new PwDatabaseV3();
            KcpPassword  pwd      = kpDatabase.MasterKey.GetUserKey <KcpPassword>();
            string       password = pwd != null?pwd.Password.ReadString() : "";

            KcpKeyFile keyfile         = kpDatabase.MasterKey.GetUserKey <KcpKeyFile>();
            Stream     keyfileContents = null;

            if (keyfile != null)
            {
                keyfileContents = new MemoryStream(keyfile.RawFileData.ReadData());
            }
            db.SetMasterKey(password, keyfileContents);

            AesKdf kdf = new AesKdf();

            if (!kdf.Uuid.Equals(kpDatabase.KdfParameters.KdfUuid))
            {
                db.NumRounds = (uint)PwDefs.DefaultKeyEncryptionRounds;
            }
            else
            {
                ulong uRounds = kpDatabase.KdfParameters.GetUInt64(
                    AesKdf.ParamRounds, PwDefs.DefaultKeyEncryptionRounds);
                uRounds = Math.Min(uRounds, 0xFFFFFFFEUL);

                db.NumRounds = (uint)uRounds;
            }


            db.Name = kpDatabase.Name;
            if (kpDatabase.DataCipherUuid.Equals(StandardAesEngine.AesUuid))
            {
                db.Algorithm = PwEncryptionAlgorithm.Rjindal;
            }
            else
            {
                db.Algorithm = PwEncryptionAlgorithm.Twofish;
            }

            //create groups
            db.Groups.Clear();
            var fromGroups = kpDatabase.RootGroup.GetGroups(true);
            Dictionary <int, PwGroupV3> groupV3s = new Dictionary <int, PwGroupV3>(fromGroups.Count());

            foreach (PwGroup g in fromGroups)
            {
                if (g == kpDatabase.RootGroup)
                {
                    continue;
                }
                PwGroupV3 groupV3 = ConvertGroup(g, db);
                db.Groups.Add(groupV3);
                groupV3s[groupV3.Id.Id] = groupV3;
            }

            //traverse again and assign parents
            db.RootGroup       = ConvertGroup(kpDatabase.RootGroup, db);
            db.RootGroup.Level = -1;

            AssignParent(kpDatabase.RootGroup, db, groupV3s);

            foreach (PwEntry e in kpDatabase.RootGroup.GetEntries(true))
            {
                PwEntryV3 entryV3 = ConvertEntry(e, db);
                entryV3.Parent = groupV3s[_groupData[e.ParentGroup.Uuid].Id];
                entryV3.Parent.ChildEntries.Add(entryV3);
                entryV3.GroupId = entryV3.Parent.Id.Id;
                db.Entries.Add(entryV3);
            }

            //add meta stream entries:
            if (db.Groups.Any())
            {
                foreach (var metaEntry in _metaStreams)
                {
                    metaEntry.GroupId = db.Groups.First().Id.Id;
                    db.Entries.Add(metaEntry);
                }
            }


            HashingStreamEx hashedStream = new HashingStreamEx(stream, true, null);
            PwDbV3Output    output       = new PwDbV3Output(db, hashedStream);

            output.Output();
            hashedStream.Close();
            HashOfLastStream = hashedStream.Hash;

            kpDatabase.HashOfLastIO = kpDatabase.HashOfFileOnDisk = HashOfLastStream;
            stream.Close();
        }
        // public void Save(string strFile, PwGroup pgDataSource, Kdb4Format 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 KDB file.
        /// </summary>
        /// <param name="sSaveTo">Stream to write the KDB 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, Kdb4Format 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 = new UTF8Encoding(false, false);
            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;
                BinaryWriter bw = null;
                if (m_format == Kdb4Format.Default)
                {
                    bw = new BinaryWriter(hashedStream, encNoBom);
                    WriteHeader(bw);                     // Also flushes bw

                    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 == Kdb4Format.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();

                GC.KeepAlive(bw);
            }
            finally { CommonCleanUpWrite(sSaveTo, hashedStream); }
        }
        // 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);
                }
            }
        }
Beispiel #26
0
        /// <summary>
        /// Load a KDB file from a stream.
        /// </summary>
        /// <param name="sSource">
        /// Stream to read the data from. Must contain
        /// a KDBX stream.
        /// </param>
        /// <param name="kdbFormat">
        /// Format specifier.
        /// </param>
        /// <param name="slLogger">
        /// Status logger (optional).
        /// </param>
        public void Load(Stream sSource, KdbxFormat kdbFormat, IStatusLogger slLogger, CancellationToken token)
        {
            Debug.Assert(sSource != null);
            if (sSource == null)
            {
                throw new ArgumentNullException("sSource");
            }

            this.m_format   = kdbFormat;
            this.m_slLogger = slLogger;

            var hashedStream = new HashingStreamEx(sSource, false, null);

            var encNoBom = StrUtil.Utf8;

            try
            {
                BinaryReaderEx br           = null;
                BinaryReaderEx brDecrypted  = null;
                Stream         readerStream = null;

                if (kdbFormat == KdbxFormat.Default)
                {
                    br = new BinaryReaderEx(hashedStream, encNoBom, KLRes.FileCorrupted);
                    this.ReadHeader(br);

                    Stream sDecrypted = this.AttachStreamDecryptor(hashedStream, token);
                    if ((sDecrypted == null) || (sDecrypted == hashedStream))
                    {
                        throw new SecurityException(KLRes.CryptoStreamFailed);
                    }

                    brDecrypted = new BinaryReaderEx(sDecrypted, encNoBom, KLRes.FileCorrupted);
                    byte[] pbStoredStartBytes = brDecrypted.ReadBytes(32);

                    if ((this.m_pbStreamStartBytes == null) || (this.m_pbStreamStartBytes.Length != 32))
                    {
                        throw new InvalidDataException();
                    }

                    for (int iStart = 0; iStart < 32; ++iStart)
                    {
                        if (pbStoredStartBytes[iStart] != this.m_pbStreamStartBytes[iStart])
                        {
                            throw new InvalidCompositeKeyException();
                        }
                    }

                    Stream sHashed = new HashedBlockStream(sDecrypted, false, 0, !this.m_bRepairMode);

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

                if (kdbFormat != KdbxFormat.PlainXml)
                {
                    // Is an encrypted format
                    if (this.m_pbProtectedStreamKey == null)
                    {
                        Debug.Assert(false);
                        throw new SecurityException("Invalid protected stream key!");
                    }

                    this.m_randomStream = new CryptoRandomStream(
                        this.m_craInnerRandomStream,
                        this.m_pbProtectedStreamKey);
                }
                else
                {
                    this.m_randomStream = null; // No random stream for plain-text files
                }

                this.ReadXmlStreamed(readerStream, hashedStream, token);

                readerStream.Dispose();
            }
            finally
            {
                this.CommonCleanUpRead(sSource, hashedStream);
            }
        }