private void OnBtnKdf1Sec(object sender, EventArgs e) { KdfEngine kdf = GetKdf(); if (kdf == null) { Debug.Assert(false); return; } if (!(kdf is AesKdf) && !(kdf is Argon2Kdf)) { return; // No assert, plugins } if (m_thKdf != null) { Debug.Assert(false); return; } try { m_thKdf = new Thread(new ParameterizedThreadStart(this.Kdf1SecTh)); EnableControlsEx(); // Disable controls (m_thKdf is not null) m_thKdf.Start(kdf); } catch (Exception) { Debug.Assert(false); m_thKdf = null; EnableControlsEx(); } }
private void Kdf1SecTh(object o) { KdfParameters p = null; string strMsg = null; try { KdfEngine kdf = (o as KdfEngine); if (kdf != null) { p = kdf.GetBestParameters(1000); } else { Debug.Assert(false); } } catch (ThreadAbortException) { try { Thread.ResetAbort(); } catch (Exception) { Debug.Assert(false); } return; } catch (Exception ex) { if ((ex != null) && !string.IsNullOrEmpty(ex.Message)) { strMsg = ex.Message; } } finally { m_thKdf = null; } // Before continuation, to enable controls try { m_btnOK.Invoke(new KdfpDelegate(this.Kdf1SecPost), p, strMsg); } catch (Exception) { Debug.Assert(false); } }
public static void Add(KdfEngine kdf) { if(kdf == null) { Debug.Assert(false); return; } EnsureInitialized(); if(Get(kdf.Uuid) != null) { Debug.Assert(false); return; } if(Get(kdf.Name) != null) { Debug.Assert(false); return; } g_l.Add(kdf); }
/// <summary> /// Generate a 32-byte (256-bit) key from the composite key. /// </summary> public ProtectedBinary GenerateKey32(KdfParameters p) { if (p == null) { Debug.Assert(false); throw new ArgumentNullException("p"); } byte[] pbRaw32 = null, pbTrf32 = null; ProtectedBinary pbRet = null; try { pbRaw32 = CreateRawCompositeKey32(); if ((pbRaw32 == null) || (pbRaw32.Length != 32)) { Debug.Assert(false); return(null); } KdfEngine kdf = KdfPool.Get(p.KdfUuid); if (kdf == null) // CryptographicExceptions are translated to "file corrupted" { throw new Exception(KLRes.UnknownKdf + MessageService.NewParagraph + KLRes.FileNewVerOrPlgReq + MessageService.NewParagraph + "UUID: " + p.KdfUuid.ToHexString() + "."); } pbTrf32 = kdf.Transform(pbRaw32, p); if (pbTrf32 == null) { Debug.Assert(false); return(null); } if (pbTrf32.Length != 32) { Debug.Assert(false); pbTrf32 = CryptoUtil.HashSha256(pbTrf32); } pbRet = new ProtectedBinary(true, pbTrf32); } finally { if (pbRaw32 != null) { MemUtil.ZeroByteArray(pbRaw32); } if (pbTrf32 != null) { MemUtil.ZeroByteArray(pbTrf32); } } return(pbRet); }
private void KdfTestTh(object o) { string strMsg = KLRes.UnknownError; try { KdfParameters p = (o as KdfParameters); if (p == null) { Debug.Assert(false); return; } KdfEngine kdf = KdfPool.Get(p.KdfUuid); if (kdf == null) { Debug.Assert(false); return; } byte[] pbMsg = new byte[32]; Program.GlobalRandom.NextBytes(pbMsg); kdf.Randomize(p); Stopwatch sw = Stopwatch.StartNew(); kdf.Transform(pbMsg, p); sw.Stop(); long lMS = sw.ElapsedMilliseconds; lMS = Math.Max(lMS, 1L); double dS = (double)lMS / 1000.0; strMsg = KPRes.TestSuccess + MessageService.NewParagraph + KPRes.TransformTime.Replace(@"{PARAM}", dS.ToString()); } catch (ThreadAbortException) { try { Thread.ResetAbort(); } catch (Exception) { Debug.Assert(false); } return; } catch (Exception ex) { Debug.Assert(false); if ((ex != null) && !string.IsNullOrEmpty(ex.Message)) { strMsg = ex.Message; } } finally { m_thKdf = null; } // Before continuation, to enable controls try { m_btnOK.Invoke(new KdfsDelegate(this.KdfTestPost), strMsg); } catch (Exception) { Debug.Assert(false); } }
private void SetKdfParameters(KdfParameters p) { if (p == null) { Debug.Assert(false); return; } KdfEngine kdf = KdfPool.Get(p.KdfUuid); if (kdf == null) { Debug.Assert(false); return; } for (int i = 0; i < m_cmbKdf.Items.Count; ++i) { string strKdf = (m_cmbKdf.Items[i] as string); if (string.IsNullOrEmpty(strKdf)) { Debug.Assert(false); continue; } if (strKdf.Equals(kdf.Name, StrUtil.CaseIgnoreCmp)) { bool bPrevInit = m_bInitializing; m_bInitializing = true; // Prevent selection handler m_cmbKdf.SelectedIndex = i; m_bInitializing = bPrevInit; break; } } if (kdf is AesKdf) { ulong uIt = p.GetUInt64(AesKdf.ParamRounds, PwDefs.DefaultKeyEncryptionRounds); SetKdfParameters(uIt, 1024, 2); } else if (kdf is Argon2Kdf) { ulong uIt = p.GetUInt64(Argon2Kdf.ParamIterations, Argon2Kdf.DefaultIterations); ulong uMem = p.GetUInt64(Argon2Kdf.ParamMemory, Argon2Kdf.DefaultMemory); uint uPar = p.GetUInt32(Argon2Kdf.ParamParallelism, Argon2Kdf.DefaultParallelism); SetKdfParameters(uIt, uMem, uPar); } // else { Debug.Assert(false); } // Plugins may provide other KDFs }
private void OnKdfSelectedIndexChanged(object sender, EventArgs e) { if (m_bInitializing) { return; } KdfEngine kdf = GetKdf(); if (kdf == null) { Debug.Assert(false); return; } SetKdfParameters(kdf.GetDefaultParameters()); EnableControlsEx(); }
// 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); } } }
private KdfParameters GetKdfParameters(bool bShowAdjustments) { KdfEngine kdf = GetKdf(); if (kdf == null) { Debug.Assert(false); return(null); } string strAdj = string.Empty; KdfParameters pKdf = kdf.GetDefaultParameters(); if (kdf is AesKdf) { pKdf.SetUInt64(AesKdf.ParamRounds, (ulong)m_numKdfIt.Value); } else if (kdf is Argon2Kdf) { ulong uIt = (ulong)m_numKdfIt.Value; AdjustKdfParam <ulong>(ref uIt, ">=", Argon2Kdf.MinIterations, KPRes.Iterations, ref strAdj); AdjustKdfParam <ulong>(ref uIt, "<=", Argon2Kdf.MaxIterations, KPRes.Iterations, ref strAdj); pKdf.SetUInt64(Argon2Kdf.ParamIterations, uIt); // Adjust parallelism first, as memory depends on it uint uPar = (uint)m_numKdfPar.Value; AdjustKdfParam <uint>(ref uPar, ">=", Argon2Kdf.MinParallelism, KPRes.Parallelism, ref strAdj); uint uParMax = Argon2Kdf.MaxParallelism; int cp = Environment.ProcessorCount; if ((cp > 0) && (cp <= (int.MaxValue / 2))) { uParMax = Math.Min(uParMax, (uint)(cp * 2)); } AdjustKdfParam <uint>(ref uPar, "<=", uParMax, KPRes.Parallelism, ref strAdj); pKdf.SetUInt32(Argon2Kdf.ParamParallelism, uPar); ulong uMem = (ulong)m_numKdfMem.Value; int iMemUnit = m_cmbKdfMem.SelectedIndex; while (iMemUnit > 0) { if (uMem > (ulong.MaxValue / 1024UL)) { uMem = ulong.MaxValue; break; } uMem *= 1024UL; --iMemUnit; } // 8*p blocks = 1024*8*p bytes minimum memory, see spec Debug.Assert(Argon2Kdf.MinMemory == (1024UL * 8UL)); ulong uMemMin = Argon2Kdf.MinMemory * uPar; AdjustKdfParam <ulong>(ref uMem, ">=", uMemMin, KPRes.Memory, ref strAdj); AdjustKdfParam <ulong>(ref uMem, "<=", Argon2Kdf.MaxMemory, KPRes.Memory, ref strAdj); pKdf.SetUInt64(Argon2Kdf.ParamMemory, uMem); } else { return(null); // Plugins may handle it } if (bShowAdjustments && (strAdj.Length > 0)) { strAdj = KPRes.KdfAdjust + MessageService.NewParagraph + strAdj; MessageService.ShowInfo(strAdj); } return(pKdf); }
private void EnableControlsEx() { if (m_bInitializing) { return; } m_btnColor.Enabled = m_cbColor.Checked; KdfEngine kdf = GetKdf(); if (kdf != null) { if (kdf is AesKdf) { m_lblKdfMem.Visible = false; m_numKdfMem.Visible = false; m_cmbKdfMem.Visible = false; m_lblKdfPar.Visible = false; m_numKdfPar.Visible = false; } else if (kdf is Argon2Kdf) { m_lblKdfMem.Visible = true; m_numKdfMem.Visible = true; m_cmbKdfMem.Visible = true; m_lblKdfPar.Visible = true; m_numKdfPar.Visible = true; } // else { Debug.Assert(false); } // Plugins may provide controls } else { Debug.Assert(false); } m_numHistoryMaxItems.Enabled = m_cbHistoryMaxItems.Checked; m_numHistoryMaxSize.Enabled = m_cbHistoryMaxSize.Checked; bool bEnableDays = ((Program.Config.UI.UIFlags & (ulong)AceUIFlags.DisableKeyChangeDays) == 0); m_numKeyRecDays.Enabled = (bEnableDays && m_cbKeyRec.Checked); m_numKeyForceDays.Enabled = (bEnableDays && m_cbKeyForce.Checked); m_cbKeyRec.Enabled = bEnableDays; m_cbKeyForce.Enabled = bEnableDays; m_cbKeyForceOnce.Enabled = bEnableDays; bool bKdfTh = (m_thKdf != null); m_tabMain.Enabled = !bKdfTh; m_pbKdf.Visible = bKdfTh; m_btnHelp.Enabled = !bKdfTh; m_btnCancelOp.Enabled = bKdfTh; m_btnCancelOp.Visible = bKdfTh; m_btnOK.Enabled = !bKdfTh; m_btnCancel.Enabled = !bKdfTh; }
// 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); } }