예제 #1
0
        public void TestAesKdf()
        {
            // Up to KeePass 2.34, the OtpKeyProv plugin used the public
            // CompositeKey.TransformKeyManaged method (and a finalizing
            // SHA-256 computation), which became an internal method of
            // the AesKdf class in KeePass 2.35, thus OtpKeyProv now
            // uses the AesKdf class; here we ensure that the results
            // are the same
            var r     = CryptoRandom.NewWeakRandom();
            var pbKey = new byte[32];

            r.NextBytes(pbKey);
            var pbSeed = new byte[32];

            r.NextBytes(pbSeed);
            var uRounds = (ulong)r.Next(1, 0x7FFF);

            var pbMan = new byte[pbKey.Length];

            Array.Copy(pbKey, pbMan, pbKey.Length);
            Assert.True(AesKdf.TransformKeyManaged(pbMan, pbSeed, uRounds));
            pbMan = CryptoUtil.HashSha256(pbMan);

            var kdf = new AesKdf();
            var p   = kdf.GetDefaultParameters();

            p.SetUInt64(AesKdf.ParamRounds, uRounds);
            p.SetByteArray(AesKdf.ParamSeed, pbSeed);
            var pbKdf = kdf.Transform(pbKey, p);

            Assert.True(MemUtil.ArraysEqual(pbMan, pbKdf));
        }
예제 #2
0
        public void BasicReferenceTest()
        {
            byte[] key  = new byte[32];
            byte[] info = new byte[32];

            var derived = new AesKdf(Common.AesFactory).GenerateBytes(key, info, 32);
        }
예제 #3
0
        private static void TestNativeKeyTransform()
        {
#if DEBUG
            byte[] pbOrgKey = CryptoRandom.Instance.GetRandomBytes(32);
            byte[] pbSeed   = CryptoRandom.Instance.GetRandomBytes(32);
            ulong  uRounds  = (ulong)((new Random()).Next(1, 0x3FFF));

            byte[] pbManaged = new byte[32];
            Array.Copy(pbOrgKey, pbManaged, 32);
            if (!AesKdf.TransformKeyManaged(pbManaged, pbSeed, uRounds))
            {
                throw new SecurityException("AES-KDF-1");
            }

            byte[] pbNative = new byte[32];
            Array.Copy(pbOrgKey, pbNative, 32);
            if (!NativeLib.TransformKey256(pbNative, pbSeed, uRounds))
            {
                return;                 // Native library not available ("success")
            }
            if (!MemUtil.ArraysEqual(pbManaged, pbNative))
            {
                throw new SecurityException("AES-KDF-2");
            }
#endif
        }
예제 #4
0
		private uint GetMinKdbxVersion()
		{
			if(m_uForceVersion != 0) return m_uForceVersion;

			// See also KeePassKdb2x3.Export (KDBX 3.1 export module)

			AesKdf kdfAes = new AesKdf();
			if(!kdfAes.Uuid.Equals(m_pwDatabase.KdfParameters.KdfUuid))
				return FileVersion32;

			if(m_pwDatabase.PublicCustomData.Count > 0)
				return FileVersion32;

			bool bCustomData = false;
			GroupHandler gh = delegate(PwGroup pg)
			{
				if(pg == null) { Debug.Assert(false); return true; }
				if(pg.CustomData.Count > 0) { bCustomData = true; return false; }
				return true;
			};
			EntryHandler eh = delegate(PwEntry pe)
			{
				if(pe == null) { Debug.Assert(false); return true; }
				if(pe.CustomData.Count > 0) { bCustomData = true; return false; }
				return true;
			};
			gh(m_pwDatabase.RootGroup);
			m_pwDatabase.RootGroup.TraverseTree(TraversalMethod.PreOrder, gh, eh);
			if(bCustomData) return FileVersion32;

			return FileVersion32_3; // KDBX 3.1 is sufficient
		}
예제 #5
0
        public void TestGenerateKey32()
        {
            var originalKey = new byte[32];
            var expectedKey = new byte[]
            {
                0xF0, 0xED, 0x57, 0xD5, 0xF0, 0xDA, 0xF3, 0x47,
                0x90, 0xD0, 0xDB, 0x43, 0x25, 0xC6, 0x81, 0x2C,
                0x81, 0x6A, 0x0D, 0x94, 0x96, 0xA9, 0x03, 0xE1,
                0x20, 0xD4, 0x3A, 0x3E, 0x45, 0xAD, 0x02, 0x65
            };
            const ulong rounds = 1;

            var           composite = new CompositeKey();
            AesKdf        kdf       = new AesKdf();
            KdfParameters p         = kdf.GetDefaultParameters();

            p.SetUInt64(AesKdf.ParamRounds, rounds);
            p.SetByteArray(AesKdf.ParamSeed, originalKey);
            var key = composite.GenerateKey32(p);

            Assert.NotNull(key);
            var keyData = key.ReadData();

            Assert.True(MemUtil.ArraysEqual(keyData, expectedKey));
        }
예제 #6
0
        private uint GetMinKdbxVersion()
        {
            if (m_uForceVersion != 0)
            {
                return(m_uForceVersion);
            }

            // See also KeePassKdb2x3.Export (KDBX 3.1 export module)
            uint minVersionForKeys = m_pwDatabase.MasterKey.UserKeys.Select(key => key.GetMinKdbxVersion()).Max();

            AesKdf kdfAes = new AesKdf();

            if (!kdfAes.Uuid.Equals(m_pwDatabase.KdfParameters.KdfUuid))
            {
                return(Math.Max(FileVersion32, minVersionForKeys));
            }

            if (m_pwDatabase.PublicCustomData.Count > 0)
            {
                return(Math.Max(FileVersion32, minVersionForKeys));
            }



            bool         bCustomData = false;
            GroupHandler gh          = delegate(PwGroup pg)
            {
                if (pg == null)
                {
                    Debug.Assert(false); return(true);
                }
                if (pg.CustomData.Count > 0)
                {
                    bCustomData = true; return(false);
                }
                return(true);
            };
            EntryHandler eh = delegate(PwEntry pe)
            {
                if (pe == null)
                {
                    Debug.Assert(false); return(true);
                }
                if (pe.CustomData.Count > 0)
                {
                    bCustomData = true; return(false);
                }
                return(true);
            };

            gh(m_pwDatabase.RootGroup);
            m_pwDatabase.RootGroup.TraverseTree(TraversalMethod.PreOrder, gh, eh);
            if (bCustomData)
            {
                return(Math.Max(FileVersion32, minVersionForKeys));
            }

            return(Math.Max(FileVersion32_3, minVersionForKeys));; // KDBX 3.1 is sufficient
        }
예제 #7
0
        public void VariableLengthInfoTest(int infoLength)
        {
            byte[] key  = new byte[32];
            byte[] info = new byte[infoLength];
            r.NextBytes(key);
            r.NextBytes(info);

            var derived = new AesKdf(Common.AesFactory).GenerateBytes(key, info, 32);
        }
예제 #8
0
        /// <summary>
        /// Generates 32 bit 2nd Key based on key option selected
        /// </summary>
        /// <param name="MasterSeed">Seed used to generate the key from m_2Key</param>
        /// <param name="TransformSeed">Seed for key transformation</param>
        /// <param name="NumRounds">Iteration count of transformation</param>
        /// <returns></returns>
        public byte[] Get2ndKey32(byte[] Hash, byte[] MasterSeed, byte[] TransformSeed)
        {
            byte[] GeneratedKey = new byte[32];

            byte[] HashBuffer = new byte[32 + 32 + 32]; // MasterSeed + DualKey + FirstStreamHash
            byte[] Key256Bits = null;
            byte[] pKey32     = null;

            try
            {
                Array.Copy(MasterSeed, 0, HashBuffer, 0, 32);

                KdfParameters KdfParams = new AesKdf().GetDefaultParameters();
                KdfParams.SetUInt64(AesKdf.ParamRounds, Key2Transformations);
                KdfParams.SetByteArray(AesKdf.ParamSeed, TransformSeed);

                var Key2nd = Get2ndKey();
                if (Key2nd == null)
                {
                    throw new SecurityException("Invalid 2nd Key");
                }

                ProtectedBinary pbinKey = Key2nd.GenerateKey32(KdfParams);
                if (pbinKey == null)
                {
                    throw new SecurityException("Invalid Key");
                }

                pKey32 = pbinKey.ReadData();
                if ((pKey32 == null) || (pKey32.Length != 32))
                {
                    throw new SecurityException("Invalid Key Data");
                }

                Array.Copy(pKey32, 0, HashBuffer, 32, 32);
                Array.Copy(Hash, 0, HashBuffer, 64, 32);

                SHA256Managed sha = new SHA256Managed();
                Key256Bits = sha.ComputeHash(HashBuffer);

                Array.Copy(Key256Bits, GeneratedKey, 32);
            }
            finally
            {
                MemUtil.ZeroByteArray(HashBuffer);
                if (Key256Bits != null)
                {
                    MemUtil.ZeroByteArray(Key256Bits);
                }
                if (pKey32 != null)
                {
                    MemUtil.ZeroByteArray(pKey32);
                }
            }

            return(GeneratedKey);
        }
예제 #9
0
        public void VariableLengthOutputTest(int outputLength)
        {
            byte[] key  = new byte[32];
            byte[] info = new byte[32];
            r.NextBytes(key);
            r.NextBytes(info);

            var derived = new AesKdf(Common.AesFactory).GenerateBytes(key, info, outputLength);
        }
예제 #10
0
        public void RandomReferenceTest(int i)
        {
            byte[] key  = new byte[32];
            byte[] info = new byte[32];
            key[0] = (byte)i; // just to get rid of a warning
            r.NextBytes(key);
            r.NextBytes(info);

            var derived = new AesKdf(Common.AesFactory).GenerateBytes(key, info, 32);
        }
예제 #11
0
        private byte[] GetKey()
        {
            byte[]       ThreeDESKey = new byte[24];
            MemoryStream ms          = new MemoryStream();

            ms.Write(m_MasterSeed, 0, 32);

            KdfParameters kdf = new AesKdf().GetDefaultParameters();

            kdf.SetUInt64(AesKdf.ParamRounds, m_NumRounds);
            kdf.SetByteArray(AesKdf.ParamSeed, m_TransformSeed);

            ProtectedBinary pbinKey = m_2Key.GenerateKey32(kdf);

            if (pbinKey == null)
            {
                throw new SecurityException("Invalid Key");
            }

            byte[] pKey32 = pbinKey.ReadData();
            if ((pKey32 == null) || (pKey32.Length != 32))
            {
                throw new SecurityException("Invalid Key Data");
            }

            ms.Write(pKey32, 0, 32);

            byte[] sRandom = m_hash.Hash;
            ms.Write(sRandom, 0, sRandom.Length);

            SHA256Managed sha = new SHA256Managed();

            byte[] Key256 = sha.ComputeHash(ms.ToArray());

            Array.Copy(Key256, ThreeDESKey, ThreeDESKey.Length);

            ms.Close();

            Array.Clear(pKey32, 0, 32);
            Array.Clear(Key256, 0, 32);

            return(ThreeDESKey);
        }
예제 #12
0
파일: OtpUtil.cs 프로젝트: 77rusa/README
        private static byte[] HashAndTransform(byte[] pbData, byte[] pbTrfKey32,
                                               ulong uTrfRounds)
        {
            SHA256Managed sha256 = new SHA256Managed();

            byte[] pbHash = sha256.ComputeHash(pbData);
            sha256.Clear();

            if (!AesKdf.TransformKeyManaged(pbHash, pbTrfKey32, uTrfRounds))
            {
                return(null);
            }

            sha256 = new SHA256Managed();
            pbHash = sha256.ComputeHash(pbHash);
            sha256.Clear();

            return(pbHash);
        }
예제 #13
0
        public void TestTransformKeyManaged()
        {
            var originalKey = new byte[32];
            var expectedKey = new byte[32]
            {
                0xDC, 0x95, 0xC0, 0x78, 0xA2, 0x40, 0x89, 0x89,
                0xAD, 0x48, 0xA2, 0x14, 0x92, 0x84, 0x20, 0x87,
                0xDC, 0x95, 0xC0, 0x78, 0xA2, 0x40, 0x89, 0x89,
                0xAD, 0x48, 0xA2, 0x14, 0x92, 0x84, 0x20, 0x87
            };
            var         seed   = new byte[32];
            const ulong rounds = 1;

            var managedKey = (byte[])originalKey.Clone();
            var success    = AesKdf.TransformKeyManaged(managedKey, seed, rounds);

            Assert.That(success, Is.True);
            Assert.That(managedKey, Is.EqualTo(expectedKey));
        }
예제 #14
0
        public ProtectedBinary GenerateKey32(byte[] pbKeySeed32, ulong uNumRounds)
        {
            Debug.Assert(pbKeySeed32 != null);
            if (pbKeySeed32 == null)
            {
                throw new ArgumentNullException("pbKeySeed32");
            }
            Debug.Assert(pbKeySeed32.Length == 32);
            if (pbKeySeed32.Length != 32)
            {
                throw new ArgumentException("pbKeySeed32");
            }

            AesKdf        kdf = new AesKdf();
            KdfParameters p   = kdf.GetDefaultParameters();

            p.SetUInt64(AesKdf.ParamRounds, uNumRounds);
            p.SetByteArray(AesKdf.ParamSeed, pbKeySeed32);

            return(GenerateKey32(p));
        }
예제 #15
0
        public override bool Export(PwExportInfo pwExportInfo, Stream sOutput,
                                    IStatusLogger slLogger)
        {
            PwDatabase pd     = pwExportInfo.ContextDatabase;
            PwGroup    pgRoot = pwExportInfo.DataGroup;

            // Remove everything that requires KDBX 4 or higher;
            // see also KdbxFile.GetMinKdbxVersion

            PwUuid puCipher = pd.DataCipherUuid;

            if (puCipher.Equals(ChaCha20Engine.ChaCha20Uuid))
            {
                pd.DataCipherUuid = StandardAesEngine.AesUuid;
            }

            KdfParameters pKdf   = pd.KdfParameters;
            AesKdf        kdfAes = new AesKdf();

            if (!pKdf.KdfUuid.Equals(kdfAes.Uuid))
            {
                pd.KdfParameters = kdfAes.GetDefaultParameters();
            }

            VariantDictionary vdPublic = pd.PublicCustomData;

            pd.PublicCustomData = new VariantDictionary();

            List <PwGroup>            lCustomGK = new List <PwGroup>();
            List <StringDictionaryEx> lCustomGV = new List <StringDictionaryEx>();
            List <PwEntry>            lCustomEK = new List <PwEntry>();
            List <StringDictionaryEx> lCustomEV = new List <StringDictionaryEx>();

            GroupHandler gh = delegate(PwGroup pg)
            {
                if (pg == null)
                {
                    Debug.Assert(false); return(true);
                }
                if (pg.CustomData.Count > 0)
                {
                    lCustomGK.Add(pg);
                    lCustomGV.Add(pg.CustomData);
                    pg.CustomData = new StringDictionaryEx();
                }
                return(true);
            };
            EntryHandler eh = delegate(PwEntry pe)
            {
                if (pe == null)
                {
                    Debug.Assert(false); return(true);
                }
                if (pe.CustomData.Count > 0)
                {
                    lCustomEK.Add(pe);
                    lCustomEV.Add(pe.CustomData);
                    pe.CustomData = new StringDictionaryEx();
                }
                return(true);
            };

            gh(pgRoot);
            pgRoot.TraverseTree(TraversalMethod.PreOrder, gh, eh);

            try
            {
                KdbxFile kdbx = new KdbxFile(pd);
                kdbx.ForceVersion = KdbxFile.FileVersion32_3_1;
                kdbx.Save(sOutput, pgRoot, KdbxFormat.Default, slLogger);
            }
            finally
            {
                // Restore

                pd.DataCipherUuid   = puCipher;
                pd.KdfParameters    = pKdf;
                pd.PublicCustomData = vdPublic;

                for (int i = 0; i < lCustomGK.Count; ++i)
                {
                    lCustomGK[i].CustomData = lCustomGV[i];
                }
                for (int i = 0; i < lCustomEK.Count; ++i)
                {
                    lCustomEK[i].CustomData = lCustomEV[i];
                }
            }

            return(true);
        }
예제 #16
0
        private bool ReadHeaderField(BinaryReaderEx brSource)
        {
            Debug.Assert(brSource != null);
            if (brSource == null)
            {
                throw new ArgumentNullException("brSource");
            }

            byte btFieldID = brSource.ReadByte();

            int cbSize;

            Debug.Assert(m_uFileVersion > 0);
            if (m_uFileVersion < FileVersion32_4)
            {
                cbSize = (int)MemUtil.BytesToUInt16(brSource.ReadBytes(2));
            }
            else
            {
                cbSize = MemUtil.BytesToInt32(brSource.ReadBytes(4));
            }
            if (cbSize < 0)
            {
                throw new FormatException(KLRes.FileCorrupted);
            }

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

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

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

            case KdbxHeaderFieldID.CipherID:
                SetCipher(pbData);
                break;

            case KdbxHeaderFieldID.CompressionFlags:
                SetCompressionFlags(pbData);
                break;

            case KdbxHeaderFieldID.MasterSeed:
                m_pbMasterSeed = pbData;
                CryptoRandom.Instance.AddEntropy(pbData);
                break;

            // Obsolete; for backward compatibility only
            case KdbxHeaderFieldID.TransformSeed:
                Debug.Assert(m_uFileVersion < FileVersion32_4);

                AesKdf kdfS = new AesKdf();
                if (!m_pwDatabase.KdfParameters.KdfUuid.Equals(kdfS.Uuid))
                {
                    m_pwDatabase.KdfParameters = kdfS.GetDefaultParameters();
                }

                // m_pbTransformSeed = pbData;
                m_pwDatabase.KdfParameters.SetByteArray(AesKdf.ParamSeed, pbData);

                CryptoRandom.Instance.AddEntropy(pbData);
                break;

            // Obsolete; for backward compatibility only
            case KdbxHeaderFieldID.TransformRounds:
                Debug.Assert(m_uFileVersion < FileVersion32_4);

                AesKdf kdfR = new AesKdf();
                if (!m_pwDatabase.KdfParameters.KdfUuid.Equals(kdfR.Uuid))
                {
                    m_pwDatabase.KdfParameters = kdfR.GetDefaultParameters();
                }

                // m_pwDatabase.KeyEncryptionRounds = MemUtil.BytesToUInt64(pbData);
                m_pwDatabase.KdfParameters.SetUInt64(AesKdf.ParamRounds,
                                                     MemUtil.BytesToUInt64(pbData));
                break;

            case KdbxHeaderFieldID.EncryptionIV:
                m_pbEncryptionIV = pbData;
                break;

            case KdbxHeaderFieldID.InnerRandomStreamKey:
                Debug.Assert(m_uFileVersion < FileVersion32_4);
                Debug.Assert(m_pbInnerRandomStreamKey == null);
                m_pbInnerRandomStreamKey = pbData;
                CryptoRandom.Instance.AddEntropy(pbData);
                break;

            case KdbxHeaderFieldID.StreamStartBytes:
                Debug.Assert(m_uFileVersion < FileVersion32_4);
                m_pbStreamStartBytes = pbData;
                break;

            case KdbxHeaderFieldID.InnerRandomStreamID:
                Debug.Assert(m_uFileVersion < FileVersion32_4);
                SetInnerRandomStreamID(pbData);
                break;

            case KdbxHeaderFieldID.KdfParameters:
                m_pwDatabase.KdfParameters = KdfParameters.DeserializeExt(pbData);
                break;

            case KdbxHeaderFieldID.PublicCustomData:
                Debug.Assert(m_pwDatabase.PublicCustomData.Count == 0);
                m_pwDatabase.PublicCustomData = VariantDictionary.Deserialize(pbData);
                break;

            default:
                Debug.Assert(false);
                if (m_slLogger != null)
                {
                    m_slLogger.SetText(KLRes.UnknownHeaderId + ": " +
                                       kdbID.ToString() + "!", LogStatusType.Warning);
                }
                break;
            }

            return(bResult);
        }
예제 #17
0
        private uint GetMinKdbxVersion()
        {
            if (m_uForceVersion != 0)
            {
                return(m_uForceVersion);
            }

            // See also KeePassKdb2x3.Export (KDBX 3.1 export module)

            uint uMin = 0;

            GroupHandler gh = delegate(PwGroup pg)
            {
                if (pg == null)
                {
                    Debug.Assert(false); return(true);
                }

                if (pg.Tags.Count != 0)
                {
                    uMin = Math.Max(uMin, FileVersion32_4_1);
                }
                if (pg.CustomData.Count != 0)
                {
                    uMin = Math.Max(uMin, FileVersion32_4);
                }

                return(true);
            };

            EntryHandler eh = delegate(PwEntry pe)
            {
                if (pe == null)
                {
                    Debug.Assert(false); return(true);
                }

                if (!pe.QualityCheck)
                {
                    uMin = Math.Max(uMin, FileVersion32_4_1);
                }
                if (pe.CustomData.Count != 0)
                {
                    uMin = Math.Max(uMin, FileVersion32_4);
                }

                return(true);
            };

            gh(m_pwDatabase.RootGroup);
            m_pwDatabase.RootGroup.TraverseTree(TraversalMethod.PreOrder, gh, eh);

            if (uMin >= FileVersion32_4_1)
            {
                return(uMin);                                      // All below is <= 4.1
            }
            foreach (PwCustomIcon ci in m_pwDatabase.CustomIcons)
            {
                if ((ci.Name.Length != 0) || ci.LastModificationTime.HasValue)
                {
                    return(FileVersion32_4_1);
                }
            }

            foreach (KeyValuePair <string, string> kvp in m_pwDatabase.CustomData)
            {
                DateTime?odt = m_pwDatabase.CustomData.GetLastModificationTime(kvp.Key);
                if (odt.HasValue)
                {
                    return(FileVersion32_4_1);
                }
            }

            if (uMin >= FileVersion32_4)
            {
                return(uMin);                                    // All below is <= 4
            }
            if (m_pwDatabase.DataCipherUuid.Equals(ChaCha20Engine.ChaCha20Uuid))
            {
                return(FileVersion32_4);
            }

            AesKdf kdfAes = new AesKdf();

            if (!m_pwDatabase.KdfParameters.KdfUuid.Equals(kdfAes.Uuid))
            {
                return(FileVersion32_4);
            }

            if (m_pwDatabase.PublicCustomData.Count != 0)
            {
                return(FileVersion32_4);
            }

            return(FileVersion32_3_1);            // KDBX 3.1 is sufficient
        }
예제 #18
0
        public void ReferenceTest(byte[] key, byte[] info, byte[] expected)
        {
            var output = new AesKdf(Common.AesFactory).GenerateBytes(key, info, expected.Length);

            Assert.Equal(expected, output);
        }
예제 #19
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();
        }
예제 #20
0
파일: KdbxFile.cs 프로젝트: t00/KeePassCore
        private uint GetMinKdbxVersion()
        {
            if (m_uForceVersion != 0)
            {
                return(m_uForceVersion);
            }

            // See also KeePassKdb2x3.Export (KDBX 3.1 export module)

            foreach (KeyValuePair <string, string> kvp in m_pwDatabase.CustomData)
            {
                DateTime?odt = m_pwDatabase.CustomData.GetLastModificationTime(kvp.Key);
                if (odt.HasValue)
                {
                    return(FileVersion32_4_1);
                }
            }

            if (m_pwDatabase.DataCipherUuid.Equals(ChaCha20Engine.ChaCha20Uuid))
            {
                return(FileVersion32_4);
            }

            AesKdf kdfAes = new AesKdf();

            if (!m_pwDatabase.KdfParameters.KdfUuid.Equals(kdfAes.Uuid))
            {
                return(FileVersion32_4);
            }

            if (m_pwDatabase.PublicCustomData.Count > 0)
            {
                return(FileVersion32_4);
            }

            bool         bCustomData = false;
            GroupHandler gh          = delegate(PwGroup pg)
            {
                if (pg == null)
                {
                    Debug.Assert(false); return(true);
                }
                if (pg.CustomData.Count > 0)
                {
                    bCustomData = true; return(false);
                }
                return(true);
            };
            EntryHandler eh = delegate(PwEntry pe)
            {
                if (pe == null)
                {
                    Debug.Assert(false); return(true);
                }
                if (pe.CustomData.Count > 0)
                {
                    bCustomData = true; return(false);
                }
                return(true);
            };

            gh(m_pwDatabase.RootGroup);
            m_pwDatabase.RootGroup.TraverseTree(TraversalMethod.PreOrder, gh, eh);
            if (bCustomData)
            {
                return(FileVersion32_4);
            }

            return(FileVersion32_3_1);            // KDBX 3.1 is sufficient
        }
예제 #21
0
        static void Main(string[] args)
        {
            int    messageCount     = 1000000;
            double clientDropChance = 0.0;
            double serverDropChance = 0.0;
            var    defaultServices  = new BouncyCastleServices(KeyGeneration.GeneratePrivateKey());

            Random r = new Random();
            RandomNumberGenerator rng = new RandomNumberGenerator();
            Stopwatch             sw  = new Stopwatch();

            Console.WriteLine("Generating data...");
            byte[] keys   = new byte[16 * 1000000];
            byte[] blocks = new byte[16 * 1000000];
            byte[] output = new byte[32 * 1000000];
            rng.Generate(keys);
            rng.Generate(blocks);

            Thread.Sleep(1000);
            {
                var poly = new Poly(defaultServices.AesFactory);
                poly.Init(new ArraySegment <byte>(keys, 0, 32), new ArraySegment <byte>(blocks, 0, 16), 16);
                poly.Init(new ArraySegment <byte>(keys, 1000, 32), new ArraySegment <byte>(blocks, 1000, 16), 16);
                Console.WriteLine("Doing Pol1305 MACs");
                sw.Reset();
                sw.Start();
                for (int i = 0; i < keys.Length; i += 32)
                {
                    poly.Process(blocks, i, 16);
                    poly.Process(blocks, i, 16);
                    poly.Process(blocks, i, 16);
                    poly.Process(blocks, i, 16);
                    poly.Compute(new ArraySegment <byte>(output, i, 16));
                }
                sw.Stop();
                Console.WriteLine($"Took {sw.Elapsed.TotalSeconds:F2}s ({(keys.Length / 32) / sw.Elapsed.TotalSeconds:F0}/s)");
            }
            Thread.Sleep(1000);
            {
                var sha = System.Security.Cryptography.SHA256.Create();
                sha.ComputeHash(blocks, 10000, 16);
                Console.WriteLine("Doing SHA256 hashes (dotnet)");
                sw.Reset();
                sw.Start();
                for (int i = 0; i < blocks.Length; i += 16)
                {
                    sha.ComputeHash(blocks, i, 16);
                }

                sw.Stop();
                Console.WriteLine($"Took {sw.Elapsed.TotalSeconds:F2}s ({(keys.Length / 16) / sw.Elapsed.TotalSeconds:F0}/s)");
            }
            Thread.Sleep(1000);
            {
                var sha = new Org.BouncyCastle.Crypto.Digests.Sha256Digest();
                sha.BlockUpdate(blocks, 10000, 16);
                sha.DoFinal(output, 10000);
                Console.WriteLine("Doing SHA256 hashes (bc)");
                sw.Reset();
                sw.Start();
                for (int i = 0; i < blocks.Length; i += 16)
                {
                    sha.BlockUpdate(blocks, i, 16);
                    sha.DoFinal(output, i * 2);
                }

                sw.Stop();
                Console.WriteLine($"Took {sw.Elapsed.TotalSeconds:F2}s ({(keys.Length / 16) / sw.Elapsed.TotalSeconds:F0}/s)");
            }

            Thread.Sleep(1000);
            {
                var aes = System.Security.Cryptography.Aes.Create();
                aes.Mode = System.Security.Cryptography.CipherMode.ECB;
                var key = new byte[16];
                Array.Copy(keys, 10000, key, 0, 16);
                aes.CreateEncryptor(key, null);
                Console.WriteLine("Calculating AES keys (dotnet)");
                sw.Reset();
                sw.Start();
                for (int i = 0; i < keys.Length; i += 16)
                {
                    Array.Copy(keys, i, key, 0, 16);
                    aes.CreateEncryptor(key, null);
                }

                sw.Stop();
                Console.WriteLine($"Took {sw.Elapsed.TotalSeconds:F2}s ({(keys.Length / 16) / sw.Elapsed.TotalSeconds:F0}/s)");
            }
            Thread.Sleep(1000);
            {
                var aes = System.Security.Cryptography.Aes.Create();
                aes.Mode = System.Security.Cryptography.CipherMode.ECB;
                var key = new byte[16];
                Array.Copy(keys, key, 16);
                var enc = aes.CreateEncryptor(key, null);
                enc.TransformBlock(blocks, 10000, 16, output, 10000);
                Console.WriteLine("Processing AES blocks (dotnet");
                sw.Reset();
                sw.Start();
                for (int i = 0; i < blocks.Length; i += 16)
                {
                    enc.TransformBlock(blocks, i, 16, output, i);
                }
                sw.Stop();
                Console.WriteLine($"Took {sw.Elapsed.TotalSeconds:F2}s ({(keys.Length / 16) / sw.Elapsed.TotalSeconds:F0}/s)");
            }
            Thread.Sleep(1000);
            {
                Org.BouncyCastle.Crypto.Engines.AesEngine aes = new Org.BouncyCastle.Crypto.Engines.AesEngine();
                aes.Init(true, new KeyParameter(keys, 10000, 16));
                aes.Init(true, new KeyParameter(keys, 20000, 16));
                Console.WriteLine("Calculating AES keys (bc)");
                sw.Reset();
                sw.Start();
                for (int i = 0; i < keys.Length; i += 16)
                {
                    aes.Init(true, new KeyParameter(keys, i, 16));
                }
                sw.Stop();
                Console.WriteLine($"Took {sw.Elapsed.TotalSeconds:F2}s ({(keys.Length / 16) / sw.Elapsed.TotalSeconds:F0}/s)");
            }
            Thread.Sleep(1000);
            {
                Org.BouncyCastle.Crypto.Engines.AesEngine aes = new Org.BouncyCastle.Crypto.Engines.AesEngine();
                aes.Init(true, new KeyParameter(keys, 12300, 16));
                aes.ProcessBlock(blocks, 10000, output, 10000);
                Console.WriteLine("Processing AES blocks (bc)");
                sw.Reset();
                sw.Start();
                for (int i = 0; i < blocks.Length; i += 16)
                {
                    aes.ProcessBlock(blocks, i, output, i);
                }
                sw.Stop();
                Console.WriteLine($"Took {sw.Elapsed.TotalSeconds:F2}s ({(keys.Length / 16) / sw.Elapsed.TotalSeconds:F0}/s)");
            }
            Thread.Sleep(1000);
            {
                var aes = new Org.BouncyCastle.Crypto.Engines.AesLightEngine();
                aes.Init(true, new KeyParameter(keys, 10000, 16));
                aes.Init(true, new KeyParameter(keys, 20000, 16));
                Console.WriteLine("Calculating AES keys (bc light)");
                sw.Reset();
                sw.Start();
                for (int i = 0; i < keys.Length; i += 16)
                {
                    aes.Init(true, new KeyParameter(keys, i, 16));
                }
                sw.Stop();
                Console.WriteLine($"Took {sw.Elapsed.TotalSeconds:F2}s ({(keys.Length / 16) / sw.Elapsed.TotalSeconds:F0}/s)");
            }
            Thread.Sleep(1000);
            {
                var aes = new Org.BouncyCastle.Crypto.Engines.AesLightEngine();
                aes.Init(true, new KeyParameter(keys, 12340, 16));
                aes.ProcessBlock(blocks, 10000, output, 10000);
                Console.WriteLine("Processing AES blocks (bc light)");
                sw.Reset();
                sw.Start();
                for (int i = 0; i < blocks.Length; i += 16)
                {
                    aes.ProcessBlock(blocks, i, output, i);
                }
                sw.Stop();
                Console.WriteLine($"Took {sw.Elapsed.TotalSeconds:F2}s ({(keys.Length / 16) / sw.Elapsed.TotalSeconds:F0}/s)");
            }

            Thread.Sleep(1000);
            {
                SymmetricRacthet sr = new SymmetricRacthet();
                var(client, server) = CreateAndInitialize();
                var kdf = new AesKdf(client.Services.AesFactory);
                sr.Initialize(rng.Generate(32));
                Console.WriteLine("Testing Symmetric Ratchet Speed");
                sr.RatchetForSending(kdf);
                sr.RatchetForSending(kdf);
                sr.RatchetForSending(kdf);
                sr.RatchetForSending(kdf);
                sw.Reset();
                sw.Start();
                int cnt = 1000000;
                for (int i = 0; i < cnt; i++)
                {
                    sr.RatchetForSending(kdf);
                }
                sw.Stop();
                Console.WriteLine($"Took {sw.Elapsed.TotalSeconds:F2}s ({cnt / sw.Elapsed.TotalSeconds:F0}/s)");
                Console.WriteLine($"It would take { (double)int.MaxValue / 2 / (cnt / sw.Elapsed.TotalSeconds) / 60:F0} minutes to do 2^32 ratchets");
            }

            Thread.Sleep(1000);
            {
                Console.WriteLine("Testing one way message send speed (small: 16 bytes)...");
                var(client, server) = CreateAndInitialize();
                var messagesToSend = Enumerable.Range(0, messageCount).Select(_ => rng.Generate(16)).ToArray();
                var messagesSent   = new List <byte[]>(messageCount);
                var m1             = client.Send(new byte[16]);
                var m2             = client.Send(new byte[16]);
                var m3             = client.Send(new byte[16]);
                sw.Reset();
                sw.Start();
                for (int i = 0; i < messageCount; i++)
                {
                    messagesSent.Add(client.Send(messagesToSend[i]));
                }
                sw.Stop();
                Console.WriteLine($"Took {sw.Elapsed.TotalSeconds:F2}s ({messageCount / sw.Elapsed.TotalSeconds:F0}/s)");
                Console.WriteLine($"Bandwidth: { messagesToSend.Sum(x => x.Length * 8) / sw.Elapsed.TotalSeconds / (1024 * 1024):F0} Mbps");


                Thread.Sleep(1000);
                Console.WriteLine("Testing one way message receive speed (small: 16 bytes)...");
                server.Receive(m1);
                server.Receive(m2);
                server.Receive(m3);
                sw.Reset();
                sw.Start();
                for (int i = 0; i < messageCount; i++)
                {
                    server.Receive(messagesSent[i]);
                }
                sw.Stop();
                Console.WriteLine($"Took {sw.Elapsed.TotalSeconds:F2}s ({messageCount / sw.Elapsed.TotalSeconds:F0}/s)");
                Console.WriteLine($"Bandwidth: { messagesToSend.Sum(x => x.Length * 8) / sw.Elapsed.TotalSeconds / (1024 * 1024):F0} Mbps");
            }

            Thread.Sleep(2000);
            {
                Console.WriteLine("Testing one way message send speed (large: 64 bytes)...");
                var(client, server) = CreateAndInitialize();
                var messagesToSend = Enumerable.Range(0, messageCount).Select(_ => rng.Generate(64)).ToArray();
                var messagesSent   = new List <byte[]>(messageCount);
                var m1             = client.Send(new byte[16]);
                var m2             = client.Send(new byte[16]);
                var m3             = client.Send(new byte[16]);
                sw.Reset();
                sw.Start();
                for (int i = 0; i < messageCount; i++)
                {
                    messagesSent.Add(client.Send(messagesToSend[i]));
                }
                sw.Stop();
                Console.WriteLine($"Took {sw.Elapsed.TotalSeconds:F2}s ({messageCount / sw.Elapsed.TotalSeconds:F0}/s)");
                Console.WriteLine($"Bandwidth: { messagesToSend.Sum(x => x.Length * 8) / sw.Elapsed.TotalSeconds / (1024 * 1024):F0} Mbps");


                Thread.Sleep(1000);
                Console.WriteLine("Testing one way message receive speed (large: 64 bytes)...");
                server.Receive(m1);
                server.Receive(m2);
                server.Receive(m3);
                sw.Reset();
                sw.Start();
                for (int i = 0; i < messageCount; i++)
                {
                    server.Receive(messagesSent[i]);
                }
                sw.Stop();
                Console.WriteLine($"Took {sw.Elapsed.TotalSeconds:F2}s ({messageCount / sw.Elapsed.TotalSeconds:F0}/s)");
                Console.WriteLine($"Bandwidth: { messagesToSend.Sum(x => x.Length * 8) / sw.Elapsed.TotalSeconds / (1024 * 1024):F0} Mbps");
            }

            messageCount /= 10;
            Thread.Sleep(2000);
            {
                Console.WriteLine("Testing one way message send speed (IP: 1350 bytes)...");
                var(client, server) = CreateAndInitialize(1350);
                var messagesToSend = Enumerable.Range(0, messageCount).Select(_ => rng.Generate(1300)).ToArray();
                var messagesSent   = new List <byte[]>(messageCount);
                var m1             = client.Send(new byte[16]);
                var m2             = client.Send(new byte[16]);
                var m3             = client.Send(new byte[16]);
                sw.Reset();
                sw.Start();
                for (int i = 0; i < messageCount; i++)
                {
                    messagesSent.Add(client.Send(messagesToSend[i]));
                }
                sw.Stop();
                Console.WriteLine($"Took {sw.Elapsed.TotalSeconds:F2}s ({messageCount / sw.Elapsed.TotalSeconds:F0}/s)");
                Console.WriteLine($"Bandwidth: { messagesToSend.Sum(x => x.Length * 8) / sw.Elapsed.TotalSeconds / (1024 * 1024):F0} Mbps");


                Thread.Sleep(1000);
                Console.WriteLine("Testing one way message receive speed (IP: 1350 bytes)...");
                server.Receive(m1);
                server.Receive(m2);
                server.Receive(m3);
                sw.Reset();
                sw.Start();
                for (int i = 0; i < messageCount; i++)
                {
                    server.Receive(messagesSent[i]);
                }
                sw.Stop();
                Console.WriteLine($"Took {sw.Elapsed.TotalSeconds:F2}s ({messageCount / sw.Elapsed.TotalSeconds:F0}/s)");
                Console.WriteLine($"Bandwidth: { messagesToSend.Sum(x => x.Length * 8) / sw.Elapsed.TotalSeconds / (1024 * 1024):F0} Mbps");
            }

            Thread.Sleep(1000);
            {
                Console.WriteLine("Testing ECDHratchet speed...");
                var(client, server) = CreateAndInitialize(1350);
                var messagesToSend = Enumerable.Range(0, messageCount / 4000).Select(_ => rng.Generate(32)).ToArray();
                server.Receive(client.Send(new byte[32]));
                client.Receive(server.Send(new byte[32]));
                sw.Reset();
                sw.Start();
                for (int i = 0; i < messageCount / 4000; i++)
                {
                    var m1 = client.Send(messagesToSend[i]);
                    server.Receive(m1);
                    var m2 = server.Send(messagesToSend[i]);
                    client.Receive(m2);
                }
                sw.Stop();
                Console.WriteLine($"Took {sw.Elapsed.TotalSeconds:F2}s ({(messageCount / 2000) / sw.Elapsed.TotalSeconds:F0}/s)");
            }

            messageCount *= 10;
            Thread.Sleep(1000);
            {
                var(client, server) = CreateAndInitialize();
                var              clientMessages           = new HashSet <byte[]>(Enumerable.Range(0, messageCount).Select(_ => rng.Generate(32)));
                var              serverMessages           = new HashSet <byte[]>(Enumerable.Range(0, messageCount).Select(_ => rng.Generate(32)));
                Queue <byte[]>   clientMessagesToSend     = new Queue <byte[]>(clientMessages);
                Queue <byte[]>   serverMessagesToSend     = new Queue <byte[]>(serverMessages);
                var              messagesSentFromClient   = new Queue <byte[]>();
                var              messagesSentFromServer   = new Queue <byte[]>();
                HashSet <byte[]> messagesReceivedByClient = new HashSet <byte[]>();
                HashSet <byte[]> messagesReceivedByServer = new HashSet <byte[]>();

                byte[] DoubleInSize(byte[] payload) => payload.Concat(payload).ToArray();

                int clientSent = 0, serverSent = 0, clientReceived = 0, serverReceived = 0, clientDropped = 0, serverDropped = 0;
                Console.WriteLine($"Sending {messageCount}/{clientDropChance:P0} and {messageCount}/{serverDropChance:P0}");

                sw.Reset();
                sw.Start();
                double oldTime = 0;
                int    oldCnt  = 0;
                for (int i = 0; ; i++)
                {
                    bool anyMessagesToReceive = messagesSentFromClient.TryPeek(out var _) || messagesSentFromServer.TryPeek(out var _);
                    bool anyMessagesToSend    = clientMessagesToSend.TryPeek(out var _) || serverMessagesToSend.TryPeek(out var _);
                    if (!anyMessagesToReceive && !anyMessagesToSend)
                    {
                        break;
                    }

                    if (i % 1000 == 0)
                    {
                        var totalReceived = clientReceived + serverReceived + clientDropped + serverDropped;
                        var totalAll      = messageCount + messageCount;
                        var percentage    = (double)totalReceived / totalAll;

                        var newTime   = sw.Elapsed.TotalSeconds;
                        var deltaTime = newTime - oldTime;
                        var deltaCnt  = totalReceived - oldCnt;

                        double perSecond = 0;
                        if (oldTime != 0)
                        {
                            perSecond = deltaCnt / deltaTime;
                        }
                        Console.Write($"\r{percentage:P0} - c: {clientSent}/{clientDropped} -> {serverReceived}  s: {serverSent}/{serverDropped} -> {clientReceived}   ({perSecond:F0}/s)  ");

                        oldCnt  = totalReceived;
                        oldTime = newTime;
                    }

                    var    clientOrServer = r.Next(2);
                    var    sendOrReceive  = r.Next(2);
                    double ratio          = (double)messageCount / messageCount;
                    int    maxClient      = 100;
                    int    maxServer      = (int)(100 / ratio);
                    var    maxMessages    = r.Next(clientOrServer == 0 ? maxClient : maxServer) + 1;

                    if (anyMessagesToSend && (sendOrReceive == 0 || !anyMessagesToReceive))
                    {
                        if (clientOrServer == 0) // send from client
                        {
                            while (maxMessages-- > 0)
                            {
                                clientMessagesToSend.TryDequeue(out var payload);
                                if (payload != null)
                                {
                                    payload = r.Next(10) > 7 ? DoubleInSize(payload) : payload;
                                    var message = client.Send(payload);
                                    if (r.NextDouble() > clientDropChance)
                                    {
                                        clientSent++;
                                        messagesSentFromClient.Enqueue(message);
                                    }
                                    else
                                    {
                                        clientDropped++;
                                    }
                                }
                            }
                        }
                        else
                        {
                            while (maxMessages-- > 0)
                            {
                                serverMessagesToSend.TryDequeue(out var payload);
                                if (payload != null)
                                {
                                    payload = r.Next(10) > 7 ? DoubleInSize(payload) : payload;
                                    var message = server.Send(payload);
                                    if (r.NextDouble() > serverDropChance)
                                    {
                                        serverSent++;
                                        messagesSentFromServer.Enqueue(message);
                                    }
                                    else
                                    {
                                        serverDropped++;
                                    }
                                }
                            }
                        }
                    }
                    else
                    {
                        if (clientOrServer != 0)  // receive by client
                        {
                            while (maxMessages-- > 0)
                            {
                                messagesSentFromServer.TryDequeue(out var message);
                                if (message != null)
                                {
                                    var payload = client.Receive(message).Payload;
                                    messagesReceivedByClient.Add(payload);
                                    clientReceived++;
                                }
                            }
                        }
                        else // receive by server
                        {
                            while (maxMessages-- > 0)
                            {
                                messagesSentFromClient.TryDequeue(out var message);
                                if (message != null)
                                {
                                    var payload = server.Receive(message).Payload;
                                    messagesReceivedByServer.Add(payload);
                                    serverReceived++;
                                }
                            }
                        }
                    }
                }

                Console.WriteLine("Done");
            }
        }
예제 #22
0
        /// <summary>
        /// Save the contents of the current <c>PwDatabase</c> to a KDB file.
        /// </summary>
        /// <param name="strSaveToFile">Location to save the KDB file to.</param>
        public void Save(string strSaveToFile, PwGroup pgDataSource)
        {
            Debug.Assert(strSaveToFile != null);
            if (strSaveToFile == null)
            {
                throw new ArgumentNullException("strSaveToFile");
            }

            using (KdbManager mgr = new KdbManager())
            {
                KdbErrorCode e = KdbFile.SetDatabaseKey(mgr, m_pwDatabase.MasterKey);
                if (e != KdbErrorCode.Success)
                {
                    Debug.Assert(false);
                    throw new Exception(KLRes.InvalidCompositeKey);
                }

                if (m_slLogger != null)
                {
                    if (m_pwDatabase.MasterKey.ContainsType(typeof(KcpUserAccount)))
                    {
                        m_slLogger.SetText(KPRes.KdbWUA, LogStatusType.Warning);
                    }

                    if (m_pwDatabase.Name.Length != 0)
                    {
                        m_slLogger.SetText(KdbPrefix + KPRes.FormatNoDatabaseName, LogStatusType.Warning);
                    }
                    if (m_pwDatabase.Description.Length != 0)
                    {
                        m_slLogger.SetText(KdbPrefix + KPRes.FormatNoDatabaseDesc, LogStatusType.Warning);
                    }
                }

                // Set properties
                AesKdf kdf = new AesKdf();
                if (!kdf.Uuid.Equals(m_pwDatabase.KdfParameters.KdfUuid))
                {
                    mgr.KeyTransformationRounds = (uint)PwDefs.DefaultKeyEncryptionRounds;
                }
                else
                {
                    ulong uRounds = m_pwDatabase.KdfParameters.GetUInt64(
                        AesKdf.ParamRounds, PwDefs.DefaultKeyEncryptionRounds);
                    uRounds = Math.Min(uRounds, 0xFFFFFFFEUL);

                    mgr.KeyTransformationRounds = (uint)uRounds;
                }

                PwGroup pgRoot = (pgDataSource ?? m_pwDatabase.RootGroup);

                // Write groups and entries
                Dictionary <PwGroup, UInt32> dictGroups = WriteGroups(mgr, pgRoot);
                WriteEntries(mgr, dictGroups, pgRoot);

                e = mgr.SaveDatabase(strSaveToFile);
                if (e != KdbErrorCode.Success)
                {
                    throw new Exception(KLRes.FileSaveFailed);
                }
            }
        }