mirror of
https://github.com/moparisthebest/keepass2android
synced 2024-08-13 17:03:49 -04:00
2e8c76d0c4
NOTE: seems like merging broke the capability to read KDBX<=3. Conflicts: src/KeePassLib2Android/Collections/AutoTypeConfig.cs src/KeePassLib2Android/Collections/ProtectedBinaryDictionary.cs src/KeePassLib2Android/Collections/ProtectedStringDictionary.cs src/KeePassLib2Android/Collections/PwObjectList.cs src/KeePassLib2Android/Collections/PwObjectPool.cs src/KeePassLib2Android/Collections/StringDictionaryEx.cs src/KeePassLib2Android/Cryptography/Cipher/CipherPool.cs src/KeePassLib2Android/Cryptography/Cipher/ICipherEngine.cs src/KeePassLib2Android/Cryptography/Cipher/Salsa20Cipher.cs src/KeePassLib2Android/Cryptography/Cipher/StandardAesEngine.cs src/KeePassLib2Android/Cryptography/CryptoRandom.cs src/KeePassLib2Android/Cryptography/CryptoRandomStream.cs src/KeePassLib2Android/Cryptography/HashingStreamEx.cs src/KeePassLib2Android/Cryptography/HmacOtp.cs src/KeePassLib2Android/Cryptography/PasswordGenerator/CharSetBasedGenerator.cs src/KeePassLib2Android/Cryptography/PasswordGenerator/CustomPwGenerator.cs src/KeePassLib2Android/Cryptography/PasswordGenerator/CustomPwGeneratorPool.cs src/KeePassLib2Android/Cryptography/PasswordGenerator/PatternBasedGenerator.cs src/KeePassLib2Android/Cryptography/PasswordGenerator/PwCharSet.cs src/KeePassLib2Android/Cryptography/PasswordGenerator/PwGenerator.cs src/KeePassLib2Android/Cryptography/PasswordGenerator/PwProfile.cs src/KeePassLib2Android/Cryptography/PopularPasswords.cs src/KeePassLib2Android/Cryptography/QualityEstimation.cs src/KeePassLib2Android/Cryptography/SelfTest.cs src/KeePassLib2Android/Delegates/Handlers.cs src/KeePassLib2Android/Interfaces/IDeepCloneable.cs src/KeePassLib2Android/Interfaces/IStatusLogger.cs src/KeePassLib2Android/Interfaces/IStructureItem.cs src/KeePassLib2Android/Interfaces/ITimeLogger.cs src/KeePassLib2Android/Interfaces/IUIOperations.cs src/KeePassLib2Android/Interfaces/IXmlSerializerEx.cs src/KeePassLib2Android/Keys/CompositeKey.cs src/KeePassLib2Android/Keys/IUserKey.cs src/KeePassLib2Android/Keys/KcpCustomKey.cs src/KeePassLib2Android/Keys/KcpKeyFile.cs src/KeePassLib2Android/Keys/KcpPassword.cs src/KeePassLib2Android/Keys/KcpUserAccount.cs src/KeePassLib2Android/Keys/KeyProvider.cs src/KeePassLib2Android/Keys/KeyProviderPool.cs src/KeePassLib2Android/Keys/KeyValidator.cs src/KeePassLib2Android/Keys/KeyValidatorPool.cs src/KeePassLib2Android/Keys/UserKeyType.cs src/KeePassLib2Android/Native/NativeLib.cs src/KeePassLib2Android/Native/NativeMethods.cs src/KeePassLib2Android/Properties/AssemblyInfo.cs src/KeePassLib2Android/PwCustomIcon.cs src/KeePassLib2Android/PwDatabase.cs src/KeePassLib2Android/PwDefs.cs src/KeePassLib2Android/PwDeletedObject.cs src/KeePassLib2Android/PwEntry.cs src/KeePassLib2Android/PwEnums.cs src/KeePassLib2Android/PwGroup.cs src/KeePassLib2Android/PwUuid.cs src/KeePassLib2Android/Resources/KLRes.Generated.cs src/KeePassLib2Android/Security/ProtectedBinary.cs src/KeePassLib2Android/Security/ProtectedString.cs src/KeePassLib2Android/Security/XorredBuffer.cs src/KeePassLib2Android/Serialization/BinaryReaderEx.cs src/KeePassLib2Android/Serialization/FileLock.cs src/KeePassLib2Android/Serialization/FileTransactionEx.cs src/KeePassLib2Android/Serialization/HashedBlockStream.cs src/KeePassLib2Android/Serialization/IOConnection.cs src/KeePassLib2Android/Serialization/IOConnectionInfo.cs src/KeePassLib2Android/Serialization/KdbxFile.Read.Streamed.cs src/KeePassLib2Android/Serialization/KdbxFile.Read.cs src/KeePassLib2Android/Serialization/KdbxFile.Write.cs src/KeePassLib2Android/Serialization/KdbxFile.cs src/KeePassLib2Android/Serialization/OldFormatException.cs src/KeePassLib2Android/Translation/KPControlCustomization.cs src/KeePassLib2Android/Translation/KPFormCustomization.cs src/KeePassLib2Android/Translation/KPStringTable.cs src/KeePassLib2Android/Translation/KPStringTableItem.cs src/KeePassLib2Android/Translation/KPTranslation.cs src/KeePassLib2Android/Translation/KPTranslationProperties.cs src/KeePassLib2Android/Utility/AppLogEx.cs src/KeePassLib2Android/Utility/GfxUtil.cs src/KeePassLib2Android/Utility/MemUtil.cs src/KeePassLib2Android/Utility/MessageService.cs src/KeePassLib2Android/Utility/StrUtil.cs src/KeePassLib2Android/Utility/TimeUtil.cs src/KeePassLib2Android/Utility/UrlUtil.cs
349 lines
9.6 KiB
C#
349 lines
9.6 KiB
C#
/*
|
|
KeePass Password Safe - The Open-Source Password Manager
|
|
Copyright (C) 2003-2016 Dominik Reichl <dominik.reichl@t-online.de>
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
using System;
|
|
using System.Diagnostics;
|
|
using System.IO;
|
|
using System.Security;
|
|
using System.Text;
|
|
using System.Xml;
|
|
|
|
#if !KeePassUAP
|
|
using System.Security.Cryptography;
|
|
#endif
|
|
|
|
using KeePassLib.Cryptography;
|
|
using KeePassLib.Resources;
|
|
using KeePassLib.Security;
|
|
using KeePassLib.Serialization;
|
|
using KeePassLib.Utility;
|
|
using InvalidDataException = KeePassLib.Serialization.InvalidDataException;
|
|
|
|
namespace KeePassLib.Keys
|
|
{
|
|
/// <summary>
|
|
/// Key files as provided by the user.
|
|
/// </summary>
|
|
public sealed class KcpKeyFile : IUserKey
|
|
{
|
|
private IOConnectionInfo m_ioc;
|
|
private ProtectedBinary m_pbKeyData;
|
|
private ProtectedBinary m_pbFileData;
|
|
|
|
/// <summary>
|
|
/// Path to the key file.
|
|
/// </summary>
|
|
public string Path
|
|
{
|
|
get { return m_ioc.Path; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get key data. Querying this property is fast (it returns a
|
|
/// reference to a cached <c>ProtectedBinary</c> object).
|
|
/// If no key data is available, <c>null</c> is returned.
|
|
/// </summary>
|
|
public ProtectedBinary KeyData
|
|
{
|
|
get { return m_pbKeyData; }
|
|
}
|
|
|
|
public IOConnectionInfo Ioc
|
|
{
|
|
get { return m_ioc; }
|
|
}
|
|
|
|
public ProtectedBinary RawFileData
|
|
{
|
|
get { return m_pbFileData; }
|
|
}
|
|
|
|
public KcpKeyFile(string strKeyFile)
|
|
{
|
|
Construct(IOConnectionInfo.FromPath(strKeyFile), false);
|
|
}
|
|
|
|
public KcpKeyFile(string strKeyFile, bool bThrowIfDbFile)
|
|
{
|
|
Construct(IOConnectionInfo.FromPath(strKeyFile), bThrowIfDbFile);
|
|
}
|
|
|
|
public KcpKeyFile(IOConnectionInfo iocKeyFile)
|
|
{
|
|
Construct(iocKeyFile, false);
|
|
}
|
|
|
|
public KcpKeyFile(IOConnectionInfo iocKeyFile, bool bThrowIfDbFile)
|
|
{
|
|
Construct(iocKeyFile, bThrowIfDbFile);
|
|
}
|
|
|
|
public KcpKeyFile(byte[] keyFileContents, IOConnectionInfo iocKeyFile, bool bThrowIfDbFile)
|
|
{
|
|
Construct(keyFileContents, iocKeyFile, bThrowIfDbFile);
|
|
}
|
|
|
|
private void Construct(byte[] pbFileData, IOConnectionInfo iocKeyFile, bool bThrowIfDbFile)
|
|
{
|
|
if (pbFileData == null) throw new Java.IO.FileNotFoundException();
|
|
m_pbFileData = new ProtectedBinary(true, pbFileData);
|
|
|
|
if(bThrowIfDbFile && (pbFileData.Length >= 8))
|
|
{
|
|
uint uSig1 = MemUtil.BytesToUInt32(MemUtil.Mid(pbFileData, 0, 4));
|
|
uint uSig2 = MemUtil.BytesToUInt32(MemUtil.Mid(pbFileData, 4, 4));
|
|
|
|
if(((uSig1 == KdbxFile.FileSignature1) &&
|
|
(uSig2 == KdbxFile.FileSignature2)) ||
|
|
((uSig1 == KdbxFile.FileSignaturePreRelease1) &&
|
|
(uSig2 == KdbxFile.FileSignaturePreRelease2)) ||
|
|
((uSig1 == KdbxFile.FileSignatureOld1) &&
|
|
(uSig2 == KdbxFile.FileSignatureOld2)))
|
|
#if KeePassLibSD
|
|
throw new Exception(KLRes.KeyFileDbSel);
|
|
#else
|
|
throw new InvalidDataException(KLRes.KeyFileDbSel);
|
|
#endif
|
|
}
|
|
|
|
byte[] pbKey = LoadXmlKeyFile(pbFileData);
|
|
if(pbKey == null) pbKey = LoadKeyFile(pbFileData);
|
|
|
|
if(pbKey == null) throw new InvalidOperationException();
|
|
|
|
m_ioc = iocKeyFile;
|
|
m_pbKeyData = new ProtectedBinary(true, pbKey);
|
|
|
|
MemUtil.ZeroByteArray(pbKey);
|
|
}
|
|
|
|
private void Construct(IOConnectionInfo iocFile, bool bThrowIfDbFile)
|
|
{
|
|
byte[] pbFileData = IOConnection.ReadFile(iocFile);
|
|
Construct(pbFileData, iocFile, bThrowIfDbFile);
|
|
}
|
|
// public void Clear()
|
|
// {
|
|
// m_strPath = string.Empty;
|
|
// m_pbKeyData = null;
|
|
// }
|
|
|
|
|
|
private static byte[] LoadKeyFile(byte[] pbFileData)
|
|
{
|
|
if(pbFileData == null) { Debug.Assert(false); return null; }
|
|
|
|
int iLength = pbFileData.Length;
|
|
|
|
byte[] pbKey = null;
|
|
if(iLength == 32) pbKey = LoadBinaryKey32(pbFileData);
|
|
else if(iLength == 64) pbKey = LoadHexKey32(pbFileData);
|
|
|
|
if(pbKey == null)
|
|
pbKey = CryptoUtil.HashSha256(pbFileData);
|
|
|
|
return pbKey;
|
|
}
|
|
|
|
private static byte[] LoadBinaryKey32(byte[] pbFileData)
|
|
{
|
|
if(pbFileData == null) { Debug.Assert(false); return null; }
|
|
if(pbFileData.Length != 32) { Debug.Assert(false); return null; }
|
|
|
|
return pbFileData;
|
|
}
|
|
|
|
private static byte[] LoadHexKey32(byte[] pbFileData)
|
|
{
|
|
if(pbFileData == null) { Debug.Assert(false); return null; }
|
|
if(pbFileData.Length != 64) { Debug.Assert(false); return null; }
|
|
|
|
try
|
|
{
|
|
if(!StrUtil.IsHexString(pbFileData, true)) return null;
|
|
|
|
string strHex = StrUtil.Utf8.GetString(pbFileData);
|
|
byte[] pbKey = MemUtil.HexStringToByteArray(strHex);
|
|
if((pbKey == null) || (pbKey.Length != 32))
|
|
{
|
|
Debug.Assert(false);
|
|
return null;
|
|
}
|
|
|
|
return pbKey;
|
|
}
|
|
catch(Exception) { Debug.Assert(false); }
|
|
|
|
return null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Create a new, random key-file.
|
|
/// </summary>
|
|
/// <param name="strFilePath">Path where the key-file should be saved to.
|
|
/// If the file exists already, it will be overwritten.</param>
|
|
/// <param name="pbAdditionalEntropy">Additional entropy used to generate
|
|
/// the random key. May be <c>null</c> (in this case only the KeePass-internal
|
|
/// random number generator is used).</param>
|
|
/// <returns>Returns a <c>FileSaveResult</c> error code.</returns>
|
|
public static void Create(string strFilePath, byte[] pbAdditionalEntropy)
|
|
{
|
|
byte[] pbKey32 = CryptoRandom.Instance.GetRandomBytes(32);
|
|
if(pbKey32 == null) throw new SecurityException();
|
|
|
|
byte[] pbFinalKey32;
|
|
if((pbAdditionalEntropy == null) || (pbAdditionalEntropy.Length == 0))
|
|
pbFinalKey32 = pbKey32;
|
|
else
|
|
{
|
|
using(MemoryStream ms = new MemoryStream())
|
|
{
|
|
MemUtil.Write(ms, pbAdditionalEntropy);
|
|
MemUtil.Write(ms, pbKey32);
|
|
|
|
pbFinalKey32 = CryptoUtil.HashSha256(ms.ToArray());
|
|
}
|
|
}
|
|
|
|
CreateXmlKeyFile(strFilePath, pbFinalKey32);
|
|
}
|
|
|
|
// ================================================================
|
|
// XML Key Files
|
|
// ================================================================
|
|
|
|
// Sample XML file:
|
|
// <?xml version="1.0" encoding="utf-8"?>
|
|
// <KeyFile>
|
|
// <Meta>
|
|
// <Version>1.00</Version>
|
|
// </Meta>
|
|
// <Key>
|
|
// <Data>ySFoKuCcJblw8ie6RkMBdVCnAf4EedSch7ItujK6bmI=</Data>
|
|
// </Key>
|
|
// </KeyFile>
|
|
|
|
private const string RootElementName = "KeyFile";
|
|
private const string MetaElementName = "Meta";
|
|
private const string VersionElementName = "Version";
|
|
private const string KeyElementName = "Key";
|
|
private const string KeyDataElementName = "Data";
|
|
|
|
private static byte[] LoadXmlKeyFile(byte[] pbFileData)
|
|
{
|
|
if(pbFileData == null) { Debug.Assert(false); return null; }
|
|
|
|
MemoryStream ms = new MemoryStream(pbFileData, false);
|
|
byte[] pbKeyData = null;
|
|
|
|
try
|
|
{
|
|
XmlDocument doc = new XmlDocument();
|
|
doc.Load(ms);
|
|
|
|
XmlElement el = doc.DocumentElement;
|
|
if((el == null) || !el.Name.Equals(RootElementName)) return null;
|
|
if(el.ChildNodes.Count < 2) return null;
|
|
|
|
foreach(XmlNode xmlChild in el.ChildNodes)
|
|
{
|
|
if(xmlChild.Name.Equals(MetaElementName)) { } // Ignore Meta
|
|
else if(xmlChild.Name == KeyElementName)
|
|
{
|
|
foreach(XmlNode xmlKeyChild in xmlChild.ChildNodes)
|
|
{
|
|
if(xmlKeyChild.Name == KeyDataElementName)
|
|
{
|
|
if(pbKeyData == null)
|
|
pbKeyData = Convert.FromBase64String(xmlKeyChild.InnerText);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch(Exception) { pbKeyData = null; }
|
|
finally { ms.Close(); }
|
|
|
|
return pbKeyData;
|
|
}
|
|
|
|
private static void CreateXmlKeyFile(string strFile, byte[] pbKeyData)
|
|
{
|
|
Debug.Assert(strFile != null);
|
|
if(strFile == null) throw new ArgumentNullException("strFile");
|
|
Debug.Assert(pbKeyData != null);
|
|
if(pbKeyData == null) throw new ArgumentNullException("pbKeyData");
|
|
|
|
IOConnectionInfo ioc = IOConnectionInfo.FromPath(strFile);
|
|
Stream sOut = IOConnection.OpenWrite(ioc);
|
|
|
|
#if KeePassUAP
|
|
XmlWriterSettings xws = new XmlWriterSettings();
|
|
xws.Encoding = StrUtil.Utf8;
|
|
xws.Indent = false;
|
|
|
|
XmlWriter xtw = XmlWriter.Create(sOut, xws);
|
|
#else
|
|
XmlTextWriter xtw = new XmlTextWriter(sOut, StrUtil.Utf8);
|
|
#endif
|
|
|
|
xtw.WriteStartDocument();
|
|
xtw.WriteWhitespace("\r\n");
|
|
xtw.WriteStartElement(RootElementName); // KeyFile
|
|
xtw.WriteWhitespace("\r\n\t");
|
|
|
|
xtw.WriteStartElement(MetaElementName); // Meta
|
|
xtw.WriteWhitespace("\r\n\t\t");
|
|
xtw.WriteStartElement(VersionElementName); // Version
|
|
xtw.WriteString("1.00");
|
|
xtw.WriteEndElement(); // End Version
|
|
xtw.WriteWhitespace("\r\n\t");
|
|
xtw.WriteEndElement(); // End Meta
|
|
xtw.WriteWhitespace("\r\n\t");
|
|
|
|
xtw.WriteStartElement(KeyElementName); // Key
|
|
xtw.WriteWhitespace("\r\n\t\t");
|
|
|
|
xtw.WriteStartElement(KeyDataElementName); // Data
|
|
xtw.WriteString(Convert.ToBase64String(pbKeyData));
|
|
xtw.WriteEndElement(); // End Data
|
|
xtw.WriteWhitespace("\r\n\t");
|
|
|
|
xtw.WriteEndElement(); // End Key
|
|
xtw.WriteWhitespace("\r\n");
|
|
|
|
xtw.WriteEndElement(); // RootElementName
|
|
xtw.WriteWhitespace("\r\n");
|
|
xtw.WriteEndDocument(); // End KeyFile
|
|
xtw.Close();
|
|
|
|
sOut.Close();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Allows to change the ioc value (without reloading the data, assuming it's the same content)
|
|
/// </summary>
|
|
/// <param name="newIoc"></param>
|
|
public void ResetIoc(IOConnectionInfo newIoc)
|
|
{
|
|
m_ioc = newIoc;
|
|
}
|
|
}
|
|
}
|