public override ProtectedString Generate(PwProfile prf, CryptoRandomStream crsRandomSource) { if (prf == null) { Debug.Assert(false); } else { Debug.Assert(prf.CustomAlgorithmUuid == Convert.ToBase64String( m_uuid.UuidBytes, Base64FormattingOptions.None)); } Random keylen = new Random((int)crsRandomSource.GetRandomUInt64()); int k = keylen.Next(3, 7); return new ProtectedString(false, cockPwdGenerator(k,keylen)); }
public override ProtectedString Generate(PwProfile prf, CryptoRandomStream crsRandomSource) { if (prf == null) Debug.Assert(false); else Debug.Assert(prf.CustomAlgorithmUuid == Convert.ToBase64String(m_uuid.UuidBytes, Base64FormattingOptions.None)); if (string.IsNullOrEmpty(settings.WordListLocation)) { System.Windows.Forms.MessageBox.Show("No word list location"); GetSettingsFromUser(); return null; } if (!System.IO.File.Exists(settings.WordListLocation)) { System.Windows.Forms.MessageBox.Show(string.Format("Word List doesn't exist at location {0}", settings.WordListLocation)); GetSettingsFromUser(); return null; } try { if (words == null) words = System.IO.File.ReadAllLines(settings.WordListLocation); StringBuilder sb = new StringBuilder(); for (int i = 0; i < settings.NumberOfWords; i++) { ulong u = crsRandomSource.GetRandomUInt64(); u %= (ulong)(words.Length - 1); if (i > 0) sb.Append(settings.Separator); string word = words[u]; if (i == 0 && word.Length > 1) word = word.Substring(0, 1).ToUpper() + word.Substring(1, word.Length - 1); sb.Append(word); } sb.Append(settings.TrailingTrash); return new ProtectedString(false, sb.ToString()); } catch (Exception ex) { System.Windows.Forms.MessageBox.Show(string.Format("Encountered this error while generating password: {0}", ex.Message)); } return null; }
private static int BenchTime(CrsAlgorithm cra, int nRounds, int nDataSize) { byte[] pbKey = new byte[4] { 0x00, 0x01, 0x02, 0x03 }; int nStart = Environment.TickCount; for (int i = 0; i < nRounds; ++i) { CryptoRandomStream c = new CryptoRandomStream(cra, pbKey); c.GetRandomBytes((uint)nDataSize); } int nEnd = Environment.TickCount; return(nEnd - nStart); }
public void ExpandPattern() { // arrange var psOutBuffer = new ProtectedString(); var pwProfile = new PwProfile(); pwProfile.Pattern = "g{5}"; var pbKey = new byte[] { 0x00 }; var crsRandomSource = new CryptoRandomStream(CrsAlgorithm.Salsa20, pbKey); var error = PatternBasedGenerator.Generate(psOutBuffer, pwProfile, crsRandomSource); // act // nothing to do as ExpandPattern() would have been called by calling Generate() // assert Assert.AreEqual(PwgError.Success, error); var actual = psOutBuffer.ReadString(); Assert.AreEqual("ggggg", actual); }
public override ProtectedString Generate(PwProfile prf, CryptoRandomStream crsRandomSource) { Random r = new Random((int)crsRandomSource.GetRandomUInt64()); var opt = new DiceWareOptions(prf.CustomAlgorithmOptions); string result = ""; int word = 0; for (int i = 0; i < 5 * opt.WordCount; i++) { word *= 10; word += (1 + r.Next(6)); if ((i + 1) % 5 == 0 && i > 0) { result += words[word]; result += " "; word = 0; } } return new ProtectedString(true, result.Trim()); }
/// <summary> /// Read the protected string and return it protected with a sequence /// of bytes generated by a random stream. /// </summary> /// <param name="crsRandomSource">Random number source.</param> /// <returns>Protected string.</returns> public byte[] ReadXorredString(CryptoRandomStream crsRandomSource) { Debug.Assert(crsRandomSource != null); if(crsRandomSource == null) throw new ArgumentNullException("crsRandomSource"); byte[] pbData = ReadUtf8(); uint uLen = (uint)pbData.Length; byte[] randomPad = crsRandomSource.GetRandomBytes(uLen); Debug.Assert(randomPad.Length == pbData.Length); for(uint i = 0; i < uLen; ++i) pbData[i] ^= randomPad[i]; return pbData; }
/// <summary> /// Initializes a new RandomNumber /// </summary> /// <param name="stream">The random stream.</param> public RandomNumber(CryptoRandomStream stream) { _stream = stream; }
/// <summary> /// Generate a random dictionary password. /// </summary> /// <param name="prf">The password profile to use.</param> /// <param name="crsRandomSource">The cryptographic stream.</param> /// <returns>A generated ProtectedString password.</returns> public override ProtectedString Generate(PwProfile prf, CryptoRandomStream crsRandomSource) { // Get the generator options. GeneratorOptions options = new GeneratorOptions(prf.CustomAlgorithmOptions); // Check if a word dictionary has already been loaded, if not, load it. if (_wordDictionary == null || _currentWordLength != options.WordLength) { _wordDictionary = ExtractWordDictionary(options.WordLength); _currentWordLength = options.WordLength; } // Get a random word from the dictionary RandomNumber randomNumber = new RandomNumber(crsRandomSource); string password = _wordDictionary.Count > 0 ? _wordDictionary[randomNumber.Next(_wordDictionary.Count)] : string.Empty; _wordDictionary.Remove(password); // Substitute characters if specified. if (options.SubstituteCharacters && !string.IsNullOrEmpty(options.SubstitutionList)) password = SubstituteCharacters(password, options.SubstitutionList); // Capitalize if necessary if (options.CapitalizationType != CapitalizationTypes.None) password = CapitalizePassword(password, options.CapitalizationType, randomNumber); return new ProtectedString(false, password); }
// 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 KeePassUAP XmlWriterSettings xws = new XmlWriterSettings(); xws.Encoding = encNoBom; xws.Indent = true; xws.IndentChars = "\t"; xws.NewLineOnAttributes = false; XmlWriter xw = XmlWriter.Create(writerStream, xws); #else XmlTextWriter xw = new XmlTextWriter(writerStream, encNoBom); xw.Formatting = Formatting.Indented; xw.IndentChar = '\t'; xw.Indentation = 1; #endif m_xmlWriter = xw; WriteDocument(pgDataSource); m_xmlWriter.Flush(); m_xmlWriter.Close(); writerStream.Close(); } finally { CommonCleanUpWrite(sSaveTo, hashedStream); } }
// 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 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, 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 = 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; 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); } }
/// <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 { BinaryReader br = null; BinaryReader brDecrypted = null; Stream readerStream = null; if(kdbFormat == Kdb4Format.Default) { br = new BinaryReader(hashedStream, encNoBom); ReadHeader(br); Stream sDecrypted = AttachStreamDecryptor(hashedStream); if((sDecrypted == null) || (sDecrypted == hashedStream)) throw new SecurityException(KLRes.CryptoStreamFailed); brDecrypted = new BinaryReader(sDecrypted, encNoBom); byte[] pbStoredStartBytes = brDecrypted.ReadBytes(32); if((pbStoredStartBytes == null) || (pbStoredStartBytes.Length != 32) || (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); 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(CrsAlgorithm.ArcFour, m_pbProtectedStreamKey); } else m_randomStream = null; // No random stream for plain text files ReadXmlStreamed(readerStream, hashedStream); // ReadXmlDom(readerStream); GC.KeepAlive(brDecrypted); GC.KeepAlive(br); CommonCleanUpRead(sSource, hashedStream); } catch(Exception) { CommonCleanUpRead(sSource, hashedStream); throw; } }
/// <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 || kdbFormat == KdbxFormat.ProtocolBuffers) { br = new BinaryReaderEx(hashedStream, encNoBom, KLRes.FileCorrupted); ReadHeader(br); Stream sDecrypted = AttachStreamDecryptor(hashedStream); if((sDecrypted == null) || (sDecrypted == hashedStream)) throw new SecurityException(KLRes.CryptoStreamFailed); if (m_slLogger != null) m_slLogger.SetText("KP2AKEY_TransformingKey", LogStatusType.AdditionalInfo); brDecrypted = new BinaryReaderEx(sDecrypted, encNoBom, KLRes.FileCorrupted); byte[] pbStoredStartBytes = brDecrypted.ReadBytes(32); if((m_pbStreamStartBytes == null) || (m_pbStreamStartBytes.Length != 32)) throw new InvalidDataException(); if (m_slLogger != null) m_slLogger.SetText("KP2AKEY_DecodingDatabase", LogStatusType.AdditionalInfo); 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 Ionic.Zlib.GZipStream(sHashed, Ionic.Zlib.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 (m_slLogger != null) m_slLogger.SetText("KP2AKEY_ParsingDatabase", LogStatusType.AdditionalInfo); var stopWatch = Stopwatch.StartNew(); if (kdbFormat == KdbxFormat.ProtocolBuffers) { KdbpFile.ReadDocument(m_pwDatabase, readerStream, m_pbProtectedStreamKey, m_pbHashOfHeader); Kp2aLog.Log(String.Format("KdbpFile.ReadDocument: {0}ms", stopWatch.ElapsedMilliseconds)); } else { ReadXmlStreamed(readerStream, hashedStream); Kp2aLog.Log(String.Format("ReadXmlStreamed: {0}ms", stopWatch.ElapsedMilliseconds)); } readerStream.Close(); // GC.KeepAlive(br); // GC.KeepAlive(brDecrypted); } catch(CryptographicException) // Thrown on invalid padding { throw new CryptographicException(KLRes.FileCorrupted); } finally { CommonCleanUpRead(sSource, hashedStream); } }
private static ProtectedString PatternGenerate(string strPattern, bool bReturnProtected, byte[] pbAdditionalEntropy) { LinkedList <char> vGenerated = new LinkedList <char>(); CryptoRandomStream crs = CreateCryptoStream(pbAdditionalEntropy); byte[] pbRandom = crs.GetRandomBytes(1024); int nRandomPos = 0; StringBuilder sb = new StringBuilder(); for (char chAdd = '~'; chAdd < 255; ++chAdd) { sb.Append(chAdd); } string strCharSetHighANSI = sb.ToString(); strPattern = PasswordGenerator.ExpandPattern(strPattern); CharStream csStream = new CharStream(strPattern); char ch = csStream.ReadChar(); while (ch != char.MinValue) { string strCurrentCharSet = string.Empty; switch (ch) { case '\\': ch = csStream.ReadChar(); if (ch == char.MinValue) // Backslash at the end { vGenerated.AddLast('\\'); break; } vGenerated.AddLast(ch); break; case 'a': strCurrentCharSet = CharSetLower + CharSetNumeric; break; case 'A': strCurrentCharSet = CharSetLower + CharSetUpper + CharSetNumeric; break; case '\u00C2': strCurrentCharSet = CharSetUpper + CharSetNumeric; break; case 'c': strCurrentCharSet = CharSetLowerConsonants; break; case 'C': strCurrentCharSet = CharSetLowerConsonants + CharSetUpperConsonants; break; case '\u0108': strCurrentCharSet = CharSetUpperConsonants; break; case 'd': strCurrentCharSet = CharSetNumeric; break; // Digit case 'h': strCurrentCharSet = CharSetHexLower; break; case 'H': strCurrentCharSet = CharSetHexUpper; break; case 'l': strCurrentCharSet = CharSetLower; break; case 'L': strCurrentCharSet = CharSetLower + CharSetUpper; break; case '\u013D': strCurrentCharSet = CharSetUpper; break; case 'p': strCurrentCharSet = CharSetPunctuation; break; case 'v': strCurrentCharSet = CharSetLowerVowel; break; case 'V': strCurrentCharSet = CharSetLowerVowel + CharSetUpperVowel; break; case '\u0177': strCurrentCharSet = CharSetUpperVowel; break; case 'x': strCurrentCharSet = strCharSetHighANSI; break; default: vGenerated.AddLast(ch); break; } if (strCurrentCharSet.Length > 0) { while (true) { byte bt = pbRandom[nRandomPos]; ++nRandomPos; if (nRandomPos == pbRandom.Length) { pbRandom = crs.GetRandomBytes(1024); nRandomPos = 0; } if (bt < (byte)strCurrentCharSet.Length) { vGenerated.AddLast(strCurrentCharSet[(int)bt]); break; } } } ch = csStream.ReadChar(); } char[] vArray = new char[vGenerated.Count]; vGenerated.CopyTo(vArray, 0); UTF8Encoding utf8 = new UTF8Encoding(); ProtectedString ps = new ProtectedString(bReturnProtected, utf8.GetBytes(vArray)); Array.Clear(vArray, 0, vArray.Length); vGenerated.Clear(); return(ps); }
private static ProtectedString CustomCharSetGenerate(CustomCharSet charSet, uint uPasswordLength, bool bReturnProtected, byte[] pbAdditionalEntropy) { Debug.Assert(charSet != null); if (charSet == null) { throw new ArgumentNullException(); } if (charSet.UCount == 0) { return(new ProtectedString(false, string.Empty)); } if (uPasswordLength == 0) { return(new ProtectedString(false, string.Empty)); } CryptoRandomStream crs = CreateCryptoStream(pbAdditionalEntropy); UTF8Encoding utf8 = new UTF8Encoding(); uint uGeneratedCount = 0, uNewCharIndex, uRandomPos = 0; byte[] pbRandom = crs.GetRandomBytes(1024); bool b16BitIndex = (charSet.UCount > 256); char[] vGenerated = new char[uPasswordLength]; Array.Clear(vGenerated, 0, vGenerated.Length); while (uGeneratedCount < uPasswordLength) { uNewCharIndex = pbRandom[uRandomPos]; ++uRandomPos; if (b16BitIndex) { uNewCharIndex *= 256; uNewCharIndex += pbRandom[uRandomPos]; ++uRandomPos; } if (uRandomPos >= (uint)pbRandom.Length) { pbRandom = CryptoRandom.GetRandomBytes(1024); uRandomPos = 0; } if (uNewCharIndex < charSet.UCount) { vGenerated[uGeneratedCount] = charSet[uNewCharIndex]; ++uGeneratedCount; } } if (Array.IndexOf <char>(vGenerated, char.MinValue) >= 0) { Debug.Assert(false); throw new System.Security.SecurityException(); } byte[] pbUTF8 = utf8.GetBytes(vGenerated); ProtectedString ps = new ProtectedString(bReturnProtected, pbUTF8); Array.Clear(pbUTF8, 0, pbUTF8.Length); Array.Clear(vGenerated, 0, vGenerated.Length); return(ps); }
private static int BenchTime(CrsAlgorithm cra, int nRounds, int nDataSize) { byte[] pbKey = new byte[4] { 0x00, 0x01, 0x02, 0x03 }; int nStart = Environment.TickCount; for(int i = 0; i < nRounds; ++i) { CryptoRandomStream c = new CryptoRandomStream(cra, pbKey); c.GetRandomBytes((uint)nDataSize); } int nEnd = Environment.TickCount; return (nEnd - nStart); }
// 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; 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"); } #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(); } finally { if(pbCipherKey != null) MemUtil.ZeroByteArray(pbCipherKey); if(pbHmacKey64 != null) MemUtil.ZeroByteArray(pbHmacKey64); CommonCleanUpWrite(lStreams, sHashing); } }
/// <summary> /// Read the protected string and return it protected with a sequence /// of bytes generated by a random stream. The object's data will be /// invisible in process memory only if the object has been initialized /// using a <c>XorredBuffer</c>. If no <c>XorredBuffer</c> has been used /// or the string has been read once already (in plain-text), the /// operation won't be secure and the protected string will be visible /// in process memory. /// </summary> /// <param name="crsRandomSource">Random number source.</param> /// <returns>Protected string.</returns> /// <exception cref="System.ArgumentNullException">Thrown if the input /// parameter is <c>null</c>.</exception> public byte[] ReadXorredString(CryptoRandomStream crsRandomSource) { Debug.Assert(crsRandomSource != null); if(crsRandomSource == null) throw new ArgumentNullException("crsRandomSource"); if(m_xbEncrypted != null) { uint uLen = m_xbEncrypted.Length; byte[] randomPad = crsRandomSource.GetRandomBytes(uLen); return m_xbEncrypted.ChangeKey(randomPad); } else // Not using XorredBuffer { byte[] pbData = ReadUtf8(); uint uLen = (uint)pbData.Length; byte[] randomPad = crsRandomSource.GetRandomBytes(uLen); Debug.Assert(randomPad.Length == uLen); for(uint i = 0; i < uLen; ++i) pbData[i] ^= randomPad[i]; return pbData; } }
// 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 || m_format == KdbxFormat.ProtocolBuffers) { 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 Ionic.Zlib.GZipStream(sHashed, Ionic.Zlib.CompressionMode.Compress); else writerStream = sHashed; } else if(m_format == KdbxFormat.PlainXml) writerStream = hashedStream; else { Debug.Assert(false); throw new FormatException("KdbFormat"); } var stopWatch = Stopwatch.StartNew(); if (m_format == KdbxFormat.ProtocolBuffers) { KdbpFile.WriteDocument(m_pwDatabase, writerStream, m_pbProtectedStreamKey, m_pbHashOfHeader); } else { m_xmlWriter = new XmlTextWriter(writerStream, encNoBom); WriteDocument(pgDataSource); m_xmlWriter.Flush(); m_xmlWriter.Close(); } writerStream.Close(); Kp2aLog.Log(String.Format("{1}: {0}ms", stopWatch.ElapsedMilliseconds, m_format == KdbxFormat.ProtocolBuffers ? "KdbpFile.WriteDocument" : "Xml WriteDocument")); } finally { CommonCleanUpWrite(sSaveTo, hashedStream); } }
/// <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.Close(); // GC.KeepAlive(br); // GC.KeepAlive(brDecrypted); } catch(CryptographicException) // Thrown on invalid padding { throw new CryptographicException(KLRes.FileCorrupted); } finally { CommonCleanUpRead(sSource, hashedStream); } }
/// <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; m_pbsBinaries.Clear(); 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 // FileStream fsOut = new FileStream("Raw.xml", FileMode.Create, // FileAccess.Write, FileShare.None); // try // { // while(true) // { // int b = sXml.ReadByte(); // if(b == -1) break; // fsOut.WriteByte((byte)b); // } // } // catch(Exception) { } // fsOut.Close(); #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 }