Files
modernkeepasslib/ModernKeePassLib/Keys/KcpKeyFile.cs
Geoffroy BONNEVILLE 26e8e5c223 Update to version 2.42.1
Some changes
Removed FutureAccesList code as it works only with UWP
2019-07-26 18:28:53 +02:00

330 lines
9.2 KiB
C#

/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2019 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 ModernKeePassLib
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using Windows.Security.Cryptography;
using Windows.Security.Cryptography.Core;
using Windows.Storage;
#else
using System.Security.Cryptography;
#endif
using ModernKeePassLib.Cryptography;
using ModernKeePassLib.Resources;
using ModernKeePassLib.Security;
using ModernKeePassLib.Serialization;
using ModernKeePassLib.Utility;
namespace ModernKeePassLib.Keys
{
/// <summary>
/// Key files as provided by the user.
/// </summary>
public sealed class KcpKeyFile : IUserKey
{
private string m_strPath;
private ProtectedBinary m_pbKeyData;
/// <summary>
/// Path to the key file.
/// </summary>
public string Path
{
get { return m_strPath; }
}
/// <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; }
}
#if ModernKeePassLib
public KcpKeyFile(StorageFile keyFile)
{
Construct(IOConnectionInfo.FromStorageFile(keyFile), false);
}
#else
public KcpKeyFile(string strKeyFile)
{
Construct(IOConnectionInfo.FromPath(strKeyFile), false);
}
public KcpKeyFile(string strKeyFile, bool bThrowIfDbFile)
{
Construct(IOConnectionInfo.FromPath(strKeyFile), bThrowIfDbFile);
}
#endif
public KcpKeyFile(IOConnectionInfo iocKeyFile)
{
Construct(iocKeyFile, false);
}
public KcpKeyFile(IOConnectionInfo iocKeyFile, bool bThrowIfDbFile)
{
Construct(iocKeyFile, bThrowIfDbFile);
}
private void Construct(IOConnectionInfo iocFile, bool bThrowIfDbFile)
{
byte[] pbFileData = IOConnection.ReadFile(iocFile);
if(pbFileData == null) throw new FileNotFoundException();
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_strPath = iocFile.Path;
m_pbKeyData = new ProtectedBinary(true, pbKey);
MemUtil.ZeroByteArray(pbKey);
}
// 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, 0, pbFileData.Length);
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>
#if ModernKeePassLib
public static void Create(StorageFile file, byte[] pbAdditionalEntropy)
#else
public static void Create(string strFilePath, byte[] pbAdditionalEntropy)
#endif
{
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());
}
}
#if ModernKeePassLib
CreateXmlKeyFile(file, pbFinalKey32);
#else
CreateXmlKeyFile(strFilePath, pbFinalKey32);
#endif
}
// ================================================================
// 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 = XmlUtilEx.CreateXmlDocument();
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.Dispose(); }
return pbKeyData;
}
#if ModernKeePassLib
private static void CreateXmlKeyFile(StorageFile file, byte[] pbKeyData)
{
Debug.Assert(file != null);
if (file == null) throw new ArgumentNullException(nameof(file));
#else
private static void CreateXmlKeyFile(string strFile, byte[] pbKeyData)
{
Debug.Assert(strFile != null);
if(strFile == null) throw new ArgumentNullException("strFile");
#endif
Debug.Assert(pbKeyData != null);
if(pbKeyData == null) throw new ArgumentNullException("pbKeyData");
#if ModernKeePassLib
var ioc = IOConnectionInfo.FromStorageFile(file);
#else
IOConnectionInfo ioc = IOConnectionInfo.FromPath(strFile);
#endif
using(Stream s = IOConnection.OpenWrite(ioc))
{
using(XmlWriter xw = XmlUtilEx.CreateXmlWriter(s))
{
xw.WriteStartDocument();
xw.WriteStartElement(RootElementName); // <KeyFile>
xw.WriteStartElement(MetaElementName); // <Meta>
xw.WriteStartElement(VersionElementName); // <Version>
xw.WriteString("1.00");
xw.WriteEndElement(); // </Version>
xw.WriteEndElement(); // </Meta>
xw.WriteStartElement(KeyElementName); // <Key>
xw.WriteStartElement(KeyDataElementName); // <Data>
xw.WriteString(Convert.ToBase64String(pbKeyData));
xw.WriteEndElement(); // </Data>
xw.WriteEndElement(); // </Key>
xw.WriteEndElement(); // </KeyFile>
xw.WriteEndDocument();
}
}
}
}
}