Files
modernkeepasslib/ModernKeePassLib/Keys/CompositeKey.cs

361 lines
9.2 KiB
C#
Raw Normal View History

2019-07-25 16:39:43 +02:00
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2020 Dominik Reichl <dominik.reichl@t-online.de>
2019-07-25 16:39:43 +02:00
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.Collections.Generic;
using System.Diagnostics;
using System.Text;
using System.Threading;
2019-07-25 16:39:43 +02:00
using ModernKeePassLib.Cryptography;
using ModernKeePassLib.Cryptography.KeyDerivation;
using ModernKeePassLib.Interfaces;
2019-07-25 16:39:43 +02:00
using ModernKeePassLib.Resources;
using ModernKeePassLib.Security;
using ModernKeePassLib.Utility;
namespace ModernKeePassLib.Keys
{
/// <summary>
/// Represents a key. A key can be build up using several user key data sources
/// like a password, a key file, the currently logged on user credentials,
/// the current computer ID, etc.
/// </summary>
public sealed class CompositeKey
{
private List<IUserKey> m_vUserKeys = new List<IUserKey>();
/// <summary>
/// List of all user keys contained in the current composite key.
/// </summary>
public IEnumerable<IUserKey> UserKeys
{
get { return m_vUserKeys; }
}
public uint UserKeyCount
{
get { return (uint)m_vUserKeys.Count; }
}
/// <summary>
/// Construct a new, empty key object.
/// </summary>
public CompositeKey()
{
}
// /// <summary>
// /// Deconstructor, clears up the key.
// /// </summary>
// ~CompositeKey()
// {
// Clear();
// }
// /// <summary>
// /// Clears the key. This function also erases all previously stored
// /// user key data objects.
// /// </summary>
// public void Clear()
// {
// foreach(IUserKey pKey in m_vUserKeys)
// pKey.Clear();
// m_vUserKeys.Clear();
// }
/// <summary>
/// Add a user key.
/// </summary>
/// <param name="pKey">User key to add.</param>
public void AddUserKey(IUserKey pKey)
{
Debug.Assert(pKey != null); if(pKey == null) throw new ArgumentNullException("pKey");
m_vUserKeys.Add(pKey);
}
/// <summary>
/// Remove a user key.
/// </summary>
/// <param name="pKey">User key to remove.</param>
/// <returns>Returns <c>true</c> if the key was removed successfully.</returns>
public bool RemoveUserKey(IUserKey pKey)
{
Debug.Assert(pKey != null); if(pKey == null) throw new ArgumentNullException("pKey");
Debug.Assert(m_vUserKeys.IndexOf(pKey) >= 0);
return m_vUserKeys.Remove(pKey);
}
/// <summary>
/// Test whether the composite key contains a specific type of
/// user keys (password, key file, ...). If at least one user
/// key of that type is present, the function returns <c>true</c>.
/// </summary>
/// <param name="tUserKeyType">User key type.</param>
/// <returns>Returns <c>true</c>, if the composite key contains
/// a user key of the specified type.</returns>
public bool ContainsType(Type tUserKeyType)
{
Debug.Assert(tUserKeyType != null);
if(tUserKeyType == null) throw new ArgumentNullException("tUserKeyType");
foreach(IUserKey pKey in m_vUserKeys)
{
if(pKey == null) { Debug.Assert(false); continue; }
2020-03-23 18:35:00 +01:00
#if ModernKeePassLib || KeePassUAP
2019-07-25 16:39:43 +02:00
if(pKey.GetType() == tUserKeyType)
return true;
#else
if(tUserKeyType.IsInstanceOfType(pKey))
return true;
#endif
}
return false;
}
/// <summary>
/// Get the first user key of a specified type.
/// </summary>
/// <param name="tUserKeyType">Type of the user key to get.</param>
/// <returns>Returns the first user key of the specified type
/// or <c>null</c> if no key of that type is found.</returns>
public IUserKey GetUserKey(Type tUserKeyType)
{
Debug.Assert(tUserKeyType != null);
if(tUserKeyType == null) throw new ArgumentNullException("tUserKeyType");
foreach(IUserKey pKey in m_vUserKeys)
{
if(pKey == null) { Debug.Assert(false); continue; }
2020-03-23 18:35:00 +01:00
#if ModernKeePassLib || KeePassUAP
2019-07-25 16:39:43 +02:00
if(pKey.GetType() == tUserKeyType)
return pKey;
#else
if(tUserKeyType.IsInstanceOfType(pKey))
return pKey;
#endif
}
return null;
}
/// <summary>
/// Creates the composite key from the supplied user key sources (password,
/// key file, user account, computer ID, etc.).
/// </summary>
private byte[] CreateRawCompositeKey32()
{
ValidateUserKeys();
List<byte[]> lData = new List<byte[]>();
int cbData = 0;
foreach(IUserKey pKey in m_vUserKeys)
{
ProtectedBinary b = pKey.KeyData;
if(b != null)
{
byte[] pbKeyData = b.ReadData();
lData.Add(pbKeyData);
cbData += pbKeyData.Length;
}
}
byte[] pbAllData = new byte[cbData];
int p = 0;
foreach(byte[] pbData in lData)
{
Array.Copy(pbData, 0, pbAllData, p, pbData.Length);
p += pbData.Length;
MemUtil.ZeroByteArray(pbData);
}
Debug.Assert(p == cbData);
byte[] pbHash = CryptoUtil.HashSha256(pbAllData);
MemUtil.ZeroByteArray(pbAllData);
return pbHash;
}
public bool EqualsValue(CompositeKey ckOther)
{
if(ckOther == null) throw new ArgumentNullException("ckOther");
bool bEqual;
2019-07-25 16:39:43 +02:00
byte[] pbThis = CreateRawCompositeKey32();
try
{
byte[] pbOther = ckOther.CreateRawCompositeKey32();
bEqual = MemUtil.ArraysEqual(pbThis, pbOther);
MemUtil.ZeroByteArray(pbOther);
}
finally { MemUtil.ZeroByteArray(pbThis); }
2019-07-25 16:39:43 +02:00
return bEqual;
2019-07-25 16:39:43 +02:00
}
[Obsolete]
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);
}
/// <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;
2019-07-25 16:39:43 +02:00
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);
}
2019-07-25 16:39:43 +02:00
pbRet = new ProtectedBinary(true, pbTrf32);
}
finally
2019-07-25 16:39:43 +02:00
{
if(pbRaw32 != null) MemUtil.ZeroByteArray(pbRaw32);
if(pbTrf32 != null) MemUtil.ZeroByteArray(pbTrf32);
2019-07-25 16:39:43 +02:00
}
return pbRet;
}
private sealed class CkGkTaskInfo
{
public volatile ProtectedBinary Key = null;
public volatile string Error = null;
}
internal ProtectedBinary GenerateKey32Ex(KdfParameters p, IStatusLogger sl)
{
2020-03-23 18:35:00 +01:00
#if ModernKeePassLib
return GenerateKey32(p);
#else
2020-05-12 12:46:25 +02:00
if(sl == null) return GenerateKey32(p);
CkGkTaskInfo ti = new CkGkTaskInfo();
ThreadStart f = delegate()
{
if(ti == null) { Debug.Assert(false); return; }
try { ti.Key = GenerateKey32(p); }
catch(ThreadAbortException exAbort)
{
ti.Error = ((exAbort != null) ? exAbort.Message : null);
Thread.ResetAbort();
}
catch(Exception ex)
{
Debug.Assert(false);
ti.Error = ((ex != null) ? ex.Message : null);
}
};
Thread th = new Thread(f);
th.Start();
Debug.Assert(PwDefs.UIUpdateDelay >= 2);
while(!th.Join(PwDefs.UIUpdateDelay / 2))
{
if(!sl.ContinueWork())
{
try { th.Abort(); }
catch(Exception) { Debug.Assert(false); }
throw new OperationCanceledException();
}
}
if(!string.IsNullOrEmpty(ti.Error)) throw new Exception(ti.Error);
Debug.Assert(ti.Key != null);
return ti.Key;
2020-03-23 18:35:00 +01:00
#endif
}
2019-07-25 16:39:43 +02:00
private void ValidateUserKeys()
{
int nAccounts = 0;
foreach(IUserKey uKey in m_vUserKeys)
{
if(uKey is KcpUserAccount)
++nAccounts;
}
if(nAccounts >= 2)
{
Debug.Assert(false);
throw new InvalidOperationException();
}
}
}
public sealed class InvalidCompositeKeyException : Exception
{
public override string Message
{
get
{
return (KLRes.InvalidCompositeKey + MessageService.NewParagraph +
KLRes.InvalidCompositeKeyHint);
2019-07-25 16:39:43 +02:00
}
}
public InvalidCompositeKeyException()
{
}
}
}