WIP Update lib to 2.37

This commit is contained in:
2017-10-20 20:02:52 +02:00
committed by BONNEVILLE Geoffroy
parent 9de9ae54da
commit d5b7845242
105 changed files with 9829 additions and 2410 deletions

View File

@@ -33,6 +33,7 @@ namespace ModernKeePass.Common
} }
} }
public string Open(string password, bool createNew = false) public string Open(string password, bool createNew = false)
{ {
var key = new CompositeKey(); var key = new CompositeKey();

View File

@@ -53,7 +53,9 @@ namespace ModernKeePass.Controls
var app = (App)Application.Current; var app = (App)Application.Current;
StatusTextBlock.Text = app.Database.Open(PasswordBox.Password, CreateNew); StatusTextBlock.Text = app.Database.Open(PasswordBox.Password, CreateNew);
if (app.Database.Status == DatabaseHelper.DatabaseStatus.Opened) if (app.Database.Status == DatabaseHelper.DatabaseStatus.Opened)
{
ValidationChecked?.Invoke(this, new PasswordEventArgs(app.Database.RootGroup)); ValidationChecked?.Invoke(this, new PasswordEventArgs(app.Database.RootGroup));
}
} }
private void PasswordBox_KeyDown(object sender, KeyRoutedEventArgs e) private void PasswordBox_KeyDown(object sender, KeyRoutedEventArgs e)

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<Package xmlns="http://schemas.microsoft.com/appx/2010/manifest" xmlns:m2="http://schemas.microsoft.com/appx/2013/manifest"> <Package xmlns="http://schemas.microsoft.com/appx/2010/manifest" xmlns:m2="http://schemas.microsoft.com/appx/2013/manifest">
<Identity Name="wismna.ModernKeePass" Publisher="CN=0719A91A-C322-4EE0-A257-E60733EECF06" Version="1.4.0.19" /> <Identity Name="wismna.ModernKeePass" Publisher="CN=0719A91A-C322-4EE0-A257-E60733EECF06" Version="1.5.0.19" />
<Properties> <Properties>
<DisplayName>ModernKeePass</DisplayName> <DisplayName>ModernKeePass</DisplayName>
<PublisherDisplayName>wismna</PublisherDisplayName> <PublisherDisplayName>wismna</PublisherDisplayName>

View File

@@ -1,18 +1,37 @@
<Page <Page
x:Class="ModernKeePass.Pages.AboutPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:viewModels="using:ModernKeePass.ViewModels"
x:Class="ModernKeePass.Pages.AboutPage"
mc:Ignorable="d"> mc:Ignorable="d">
<Page.DataContext>
<viewModels:AboutVm/>
</Page.DataContext>
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<TextBlock Style="{StaticResource HeaderTextBlockStyle}" Margin="0,-20,0,20">About</TextBlock> <TextBlock Style="{StaticResource HeaderTextBlockStyle}" Margin="0,-20,0,20">
<TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{Binding }" /> <Run Text="About"/>
<TextBlock Style="{StaticResource BodyTextBlockStyle}" Margin="20,0,0,0">A modern password manager for the Windows Store</TextBlock> </TextBlock>
<TextBlock Style="{StaticResource BodyTextBlockStyle}" Margin="20,0,0,0">Homepage: <Hyperlink NavigateUri="https://github.com/wismna/ModernKeePass">https://github.com/wismna/ModernKeePass</Hyperlink></TextBlock> <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{Binding Version}" />
<TextBlock Style="{StaticResource BodyTextBlockStyle}">Credits:</TextBlock> <TextBlock Style="{StaticResource BodyTextBlockStyle}" Margin="20,0,0,0">
<TextBlock Style="{StaticResource BodyTextBlockStyle}" Margin="20,0,0,0">Dominik Reichl for the KeePass application and file format</TextBlock> <Run Text="A modern password manager for the Windows Store"/>
<TextBlock Style="{StaticResource BodyTextBlockStyle}" Margin="20,0,0,0">ArtjomP for his PCL adapatation of the KeePass Library</TextBlock> </TextBlock>
<TextBlock Style="{StaticResource BodyTextBlockStyle}" Margin="20,0,0,0">
<Run Text="Homepage: "/>
<Hyperlink NavigateUri="https://github.com/wismna/ModernKeePass">
<Run Text="https://github.com/wismna/ModernKeePass"/>
</Hyperlink></TextBlock>
<TextBlock Style="{StaticResource BodyTextBlockStyle}">
<Run Text="Credits:"/>
</TextBlock>
<TextBlock Style="{StaticResource BodyTextBlockStyle}" Margin="20,0,0,0">
<Run Text="Dominik Reichl for the KeePass application and file format"/>
</TextBlock>
<TextBlock Style="{StaticResource BodyTextBlockStyle}" Margin="20,0,0,0">
<Run Text="ArtjomP for his PCL adapatation of the KeePass Library"/>
</TextBlock>
</StackPanel> </StackPanel>
</Page> </Page>

View File

@@ -9,10 +9,9 @@ namespace ModernKeePass.ViewModels
get get
{ {
var package = Package.Current; var package = Package.Current;
var packageId = package.Id; var version = package.Id.Version;
var version = packageId.Version;
return $"ModernKeePass version {version.Major}.{version.Minor}"; return $"{package.DisplayName} version {version.Major}.{version.Minor}";
} }
} }
} }

View File

@@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2014 Dominik Reichl <dominik.reichl@t-online.de> Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by

View File

@@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2014 Dominik Reichl <dominik.reichl@t-online.de> Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by

View File

@@ -0,0 +1,174 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2017 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.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using ModernKeePassLib.Delegates;
using ModernKeePassLib.Security;
namespace ModernKeePassLib.Collections
{
internal sealed class ProtectedBinarySet : IEnumerable<KeyValuePair<int, ProtectedBinary>>
{
private Dictionary<int, ProtectedBinary> m_d =
new Dictionary<int, ProtectedBinary>();
public ProtectedBinarySet()
{
}
IEnumerator IEnumerable.GetEnumerator()
{
return m_d.GetEnumerator();
}
public IEnumerator<KeyValuePair<int, ProtectedBinary>> GetEnumerator()
{
return m_d.GetEnumerator();
}
public void Clear()
{
m_d.Clear();
}
private int GetFreeID()
{
int i = m_d.Count;
while(m_d.ContainsKey(i)) { ++i; }
Debug.Assert(i == m_d.Count); // m_d.Count should be free
return i;
}
public ProtectedBinary Get(int iID)
{
ProtectedBinary pb;
if(m_d.TryGetValue(iID, out pb)) return pb;
// Debug.Assert(false); // No assert
return null;
}
public int Find(ProtectedBinary pb)
{
if(pb == null) { Debug.Assert(false); return -1; }
// Fast search by reference
foreach(KeyValuePair<int, ProtectedBinary> kvp in m_d)
{
if(object.ReferenceEquals(pb, kvp.Value))
{
Debug.Assert(pb.Equals(kvp.Value));
return kvp.Key;
}
}
// Slow search by content
foreach(KeyValuePair<int, ProtectedBinary> kvp in m_d)
{
if(pb.Equals(kvp.Value)) return kvp.Key;
}
// Debug.Assert(false); // No assert
return -1;
}
public void Set(int iID, ProtectedBinary pb)
{
if(iID < 0) { Debug.Assert(false); return; }
if(pb == null) { Debug.Assert(false); return; }
m_d[iID] = pb;
}
public void Add(ProtectedBinary pb)
{
if(pb == null) { Debug.Assert(false); return; }
int i = Find(pb);
if(i >= 0) return; // Exists already
i = GetFreeID();
m_d[i] = pb;
}
public void AddFrom(ProtectedBinaryDictionary d)
{
if(d == null) { Debug.Assert(false); return; }
foreach(KeyValuePair<string, ProtectedBinary> kvp in d)
{
Add(kvp.Value);
}
}
public void AddFrom(PwGroup pg)
{
if(pg == null) { Debug.Assert(false); return; }
EntryHandler eh = delegate(PwEntry pe)
{
if(pe == null) { Debug.Assert(false); return true; }
AddFrom(pe.Binaries);
foreach(PwEntry peHistory in pe.History)
{
if(peHistory == null) { Debug.Assert(false); continue; }
AddFrom(peHistory.Binaries);
}
return true;
};
pg.TraverseTree(TraversalMethod.PreOrder, null, eh);
}
public ProtectedBinary[] ToArray()
{
int n = m_d.Count;
ProtectedBinary[] v = new ProtectedBinary[n];
foreach(KeyValuePair<int, ProtectedBinary> kvp in m_d)
{
if((kvp.Key < 0) || (kvp.Key >= n))
{
Debug.Assert(false);
throw new InvalidOperationException();
}
v[kvp.Key] = kvp.Value;
}
for(int i = 0; i < n; ++i)
{
if(v[i] == null)
{
Debug.Assert(false);
throw new InvalidOperationException();
}
}
return v;
}
}
}

View File

@@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2014 Dominik Reichl <dominik.reichl@t-online.de> Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -283,11 +283,7 @@ namespace ModernKeePassLib.Collections
public List<string> GetKeys() public List<string> GetKeys()
{ {
List<string> v = new List<string>(); return new List<string>(m_vStrings.Keys);
foreach(string strKey in m_vStrings.Keys) v.Add(strKey);
return v;
} }
public void EnableProtection(string strField, bool bProtect) public void EnableProtection(string strField, bool bProtect)
@@ -299,7 +295,8 @@ namespace ModernKeePassLib.Collections
{ {
byte[] pbData = ps.ReadUtf8(); byte[] pbData = ps.ReadUtf8();
Set(strField, new ProtectedString(bProtect, pbData)); Set(strField, new ProtectedString(bProtect, pbData));
MemUtil.ZeroByteArray(pbData);
if(bProtect) MemUtil.ZeroByteArray(pbData);
} }
} }
} }

View File

@@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2014 Dominik Reichl <dominik.reichl@t-online.de> Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by

View File

@@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2014 Dominik Reichl <dominik.reichl@t-online.de> Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -18,12 +18,14 @@
*/ */
using System; using System;
using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text;
using System.Diagnostics; using System.Diagnostics;
using System.Text;
using ModernKeePassLib.Delegates; using ModernKeePassLib.Delegates;
using ModernKeePassLib.Interfaces; using ModernKeePassLib.Interfaces;
using ModernKeePassLib.Utility;
#if KeePassLibSD #if KeePassLibSD
using KeePassLibSD; using KeePassLibSD;
@@ -77,4 +79,154 @@ namespace ModernKeePassLib.Collections
return true; return true;
} }
} }
internal sealed class PwObjectPoolEx
{
private Dictionary<PwUuid, ulong> m_dUuidToId =
new Dictionary<PwUuid, ulong>();
private Dictionary<ulong, IStructureItem> m_dIdToItem =
new Dictionary<ulong, IStructureItem>();
private PwObjectPoolEx()
{
}
public static PwObjectPoolEx FromGroup(PwGroup pg)
{
PwObjectPoolEx p = new PwObjectPoolEx();
if(pg == null) { Debug.Assert(false); return p; }
ulong uFreeId = 2; // 0 = "not found", 1 is a hole
p.m_dUuidToId[pg.Uuid] = uFreeId;
p.m_dIdToItem[uFreeId] = pg;
uFreeId += 2; // Make hole
p.AddGroupRec(pg, ref uFreeId);
return p;
}
private void AddGroupRec(PwGroup pg, ref ulong uFreeId)
{
if(pg == null) { Debug.Assert(false); return; }
ulong uId = uFreeId;
// Consecutive entries must have consecutive IDs
foreach(PwEntry pe in pg.Entries)
{
Debug.Assert(!m_dUuidToId.ContainsKey(pe.Uuid));
Debug.Assert(!m_dIdToItem.ContainsValue(pe));
m_dUuidToId[pe.Uuid] = uId;
m_dIdToItem[uId] = pe;
++uId;
}
++uId; // Make hole
// Consecutive groups must have consecutive IDs
foreach(PwGroup pgSub in pg.Groups)
{
Debug.Assert(!m_dUuidToId.ContainsKey(pgSub.Uuid));
Debug.Assert(!m_dIdToItem.ContainsValue(pgSub));
m_dUuidToId[pgSub.Uuid] = uId;
m_dIdToItem[uId] = pgSub;
++uId;
}
++uId; // Make hole
foreach(PwGroup pgSub in pg.Groups)
{
AddGroupRec(pgSub, ref uId);
}
uFreeId = uId;
}
public ulong GetIdByUuid(PwUuid pwUuid)
{
if(pwUuid == null) { Debug.Assert(false); return 0; }
ulong uId;
m_dUuidToId.TryGetValue(pwUuid, out uId);
return uId;
}
public IStructureItem GetItemByUuid(PwUuid pwUuid)
{
if(pwUuid == null) { Debug.Assert(false); return null; }
ulong uId;
if(!m_dUuidToId.TryGetValue(pwUuid, out uId)) return null;
Debug.Assert(uId != 0);
return GetItemById(uId);
}
public IStructureItem GetItemById(ulong uId)
{
IStructureItem p;
m_dIdToItem.TryGetValue(uId, out p);
return p;
}
}
internal sealed class PwObjectBlock<T> : IEnumerable<T>
where T : class, ITimeLogger, IStructureItem, IDeepCloneable<T>
{
private List<T> m_l = new List<T>();
public T PrimaryItem
{
get { return ((m_l.Count > 0) ? m_l[0] : null); }
}
private DateTime m_dtLocationChanged = TimeUtil.SafeMinValueUtc;
public DateTime LocationChanged
{
get { return m_dtLocationChanged; }
}
private PwObjectPoolEx m_poolAssoc = null;
public PwObjectPoolEx PoolAssoc
{
get { return m_poolAssoc; }
}
public PwObjectBlock()
{
}
#if DEBUG
public override string ToString()
{
return ("PwObjectBlock, Count = " + m_l.Count.ToString());
}
#endif
IEnumerator IEnumerable.GetEnumerator()
{
return m_l.GetEnumerator();
}
public IEnumerator<T> GetEnumerator()
{
return m_l.GetEnumerator();
}
public void Add(T t, DateTime dtLoc, PwObjectPoolEx pool)
{
if(t == null) { Debug.Assert(false); return; }
m_l.Add(t);
if(dtLoc > m_dtLocationChanged)
{
m_dtLocationChanged = dtLoc;
m_poolAssoc = pool;
}
}
}
} }

View File

@@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2014 Dominik Reichl <dominik.reichl@t-online.de> Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -32,14 +32,14 @@ using KeePassLibSD;
namespace ModernKeePassLib.Collections namespace ModernKeePassLib.Collections
{ {
public sealed class StringDictionaryEx : IDeepCloneable<StringDictionaryEx>, public sealed class StringDictionaryEx : IDeepCloneable<StringDictionaryEx>,
IEnumerable<KeyValuePair<string, string>> IEnumerable<KeyValuePair<string, string>>, IEquatable<StringDictionaryEx>
{ {
private SortedDictionary<string, string> m_vDict = private SortedDictionary<string, string> m_dict =
new SortedDictionary<string, string>(); new SortedDictionary<string, string>();
public int Count public int Count
{ {
get { return m_vDict.Count; } get { return m_dict.Count; }
} }
public StringDictionaryEx() public StringDictionaryEx()
@@ -48,39 +48,53 @@ namespace ModernKeePassLib.Collections
IEnumerator IEnumerable.GetEnumerator() IEnumerator IEnumerable.GetEnumerator()
{ {
return m_vDict.GetEnumerator(); return m_dict.GetEnumerator();
} }
public IEnumerator<KeyValuePair<string, string>> GetEnumerator() public IEnumerator<KeyValuePair<string, string>> GetEnumerator()
{ {
return m_vDict.GetEnumerator(); return m_dict.GetEnumerator();
} }
public StringDictionaryEx CloneDeep() public StringDictionaryEx CloneDeep()
{ {
StringDictionaryEx plNew = new StringDictionaryEx(); StringDictionaryEx sdNew = new StringDictionaryEx();
foreach(KeyValuePair<string, string> kvpStr in m_vDict) foreach(KeyValuePair<string, string> kvp in m_dict)
plNew.Set(kvpStr.Key, kvpStr.Value); sdNew.m_dict[kvp.Key] = kvp.Value; // Strings are immutable
return plNew; return sdNew;
}
public bool Equals(StringDictionaryEx sdOther)
{
if(sdOther == null) { Debug.Assert(false); return false; }
if(m_dict.Count != sdOther.m_dict.Count) return false;
foreach(KeyValuePair<string, string> kvp in sdOther.m_dict)
{
string str = Get(kvp.Key);
if((str == null) || (str != kvp.Value)) return false;
}
return true;
} }
public string Get(string strName) public string Get(string strName)
{ {
Debug.Assert(strName != null); if(strName == null) throw new ArgumentNullException("strName"); if(strName == null) { Debug.Assert(false); throw new ArgumentNullException("strName"); }
string s; string s;
if(m_vDict.TryGetValue(strName, out s)) return s; if(m_dict.TryGetValue(strName, out s)) return s;
return null; return null;
} }
public bool Exists(string strName) public bool Exists(string strName)
{ {
Debug.Assert(strName != null); if(strName == null) throw new ArgumentNullException("strName"); if(strName == null) { Debug.Assert(false); throw new ArgumentNullException("strName"); }
return m_vDict.ContainsKey(strName); return m_dict.ContainsKey(strName);
} }
/// <summary> /// <summary>
@@ -92,25 +106,25 @@ namespace ModernKeePassLib.Collections
/// parameters is <c>null</c>.</exception> /// parameters is <c>null</c>.</exception>
public void Set(string strField, string strNewValue) public void Set(string strField, string strNewValue)
{ {
Debug.Assert(strField != null); if(strField == null) throw new ArgumentNullException("strField"); if(strField == null) { Debug.Assert(false); throw new ArgumentNullException("strField"); }
Debug.Assert(strNewValue != null); if(strNewValue == null) throw new ArgumentNullException("strNewValue"); if(strNewValue == null) { Debug.Assert(false); throw new ArgumentNullException("strNewValue"); }
m_vDict[strField] = strNewValue; m_dict[strField] = strNewValue;
} }
/// <summary> /// <summary>
/// Delete a string. /// Delete a string.
/// </summary> /// </summary>
/// <param name="strField">Name of the string field to delete.</param> /// <param name="strField">Name of the string field to delete.</param>
/// <returns>Returns <c>true</c> if the field has been successfully /// <returns>Returns <c>true</c>, if the field has been successfully
/// removed, otherwise the return value is <c>false</c>.</returns> /// removed. Otherwise, the return value is <c>false</c>.</returns>
/// <exception cref="System.ArgumentNullException">Thrown if the input /// <exception cref="System.ArgumentNullException">Thrown if the input
/// parameter is <c>null</c>.</exception> /// parameter is <c>null</c>.</exception>
public bool Remove(string strField) public bool Remove(string strField)
{ {
Debug.Assert(strField != null); if(strField == null) throw new ArgumentNullException("strField"); if(strField == null) { Debug.Assert(false); throw new ArgumentNullException("strField"); }
return m_vDict.Remove(strField); return m_dict.Remove(strField);
} }
} }
} }

View File

@@ -0,0 +1,415 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2017 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.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;
using ModernKeePassLib.Resources;
using ModernKeePassLib.Utility;
namespace ModernKeePassLib.Collections
{
public class VariantDictionary
{
private const ushort VdVersion = 0x0100;
private const ushort VdmCritical = 0xFF00;
private const ushort VdmInfo = 0x00FF;
private Dictionary<string, object> m_d = new Dictionary<string, object>();
private enum VdType : byte
{
None = 0,
// Byte = 0x02,
// UInt16 = 0x03,
UInt32 = 0x04,
UInt64 = 0x05,
// Signed mask: 0x08
Bool = 0x08,
// SByte = 0x0A,
// Int16 = 0x0B,
Int32 = 0x0C,
Int64 = 0x0D,
// Float = 0x10,
// Double = 0x11,
// Decimal = 0x12,
// Char = 0x17, // 16-bit Unicode character
String = 0x18,
// Array mask: 0x40
ByteArray = 0x42
}
public int Count
{
get { return m_d.Count; }
}
public VariantDictionary()
{
Debug.Assert((VdmCritical & VdmInfo) == ushort.MinValue);
Debug.Assert((VdmCritical | VdmInfo) == ushort.MaxValue);
}
private bool Get<T>(string strName, out T t)
{
t = default(T);
if(string.IsNullOrEmpty(strName)) { Debug.Assert(false); return false; }
object o;
if(!m_d.TryGetValue(strName, out o)) return false; // No assert
if(o == null) { Debug.Assert(false); return false; }
if(o.GetType() != typeof(T)) { Debug.Assert(false); return false; }
t = (T)o;
return true;
}
private void SetStruct<T>(string strName, T t)
where T : struct
{
if(string.IsNullOrEmpty(strName)) { Debug.Assert(false); return; }
#if DEBUG
T tEx;
Get<T>(strName, out tEx); // Assert same type
#endif
m_d[strName] = t;
}
private void SetRef<T>(string strName, T t)
where T : class
{
if(string.IsNullOrEmpty(strName)) { Debug.Assert(false); return; }
if(t == null) { Debug.Assert(false); return; }
#if DEBUG
T tEx;
Get<T>(strName, out tEx); // Assert same type
#endif
m_d[strName] = t;
}
public bool Remove(string strName)
{
if(string.IsNullOrEmpty(strName)) { Debug.Assert(false); return false; }
return m_d.Remove(strName);
}
public void CopyTo(VariantDictionary d)
{
if(d == null) { Debug.Assert(false); return; }
// Do not clear the target
foreach(KeyValuePair<string, object> kvp in m_d)
{
d.m_d[kvp.Key] = kvp.Value;
}
}
public Type GetTypeOf(string strName)
{
if(string.IsNullOrEmpty(strName)) { Debug.Assert(false); return null; }
object o;
m_d.TryGetValue(strName, out o);
if(o == null) return null; // No assert
return o.GetType();
}
public uint GetUInt32(string strName, uint uDefault)
{
uint u;
if(Get<uint>(strName, out u)) return u;
return uDefault;
}
public void SetUInt32(string strName, uint uValue)
{
SetStruct<uint>(strName, uValue);
}
public ulong GetUInt64(string strName, ulong uDefault)
{
ulong u;
if(Get<ulong>(strName, out u)) return u;
return uDefault;
}
public void SetUInt64(string strName, ulong uValue)
{
SetStruct<ulong>(strName, uValue);
}
public bool GetBool(string strName, bool bDefault)
{
bool b;
if(Get<bool>(strName, out b)) return b;
return bDefault;
}
public void SetBool(string strName, bool bValue)
{
SetStruct<bool>(strName, bValue);
}
public int GetInt32(string strName, int iDefault)
{
int i;
if(Get<int>(strName, out i)) return i;
return iDefault;
}
public void SetInt32(string strName, int iValue)
{
SetStruct<int>(strName, iValue);
}
public long GetInt64(string strName, long lDefault)
{
long l;
if(Get<long>(strName, out l)) return l;
return lDefault;
}
public void SetInt64(string strName, long lValue)
{
SetStruct<long>(strName, lValue);
}
public string GetString(string strName)
{
string str;
Get<string>(strName, out str);
return str;
}
public void SetString(string strName, string strValue)
{
SetRef<string>(strName, strValue);
}
public byte[] GetByteArray(string strName)
{
byte[] pb;
Get<byte[]>(strName, out pb);
return pb;
}
public void SetByteArray(string strName, byte[] pbValue)
{
SetRef<byte[]>(strName, pbValue);
}
/// <summary>
/// Create a deep copy.
/// </summary>
public virtual object Clone()
{
VariantDictionary vdNew = new VariantDictionary();
foreach(KeyValuePair<string, object> kvp in m_d)
{
object o = kvp.Value;
if(o == null) { Debug.Assert(false); continue; }
Type t = o.GetType();
if(t == typeof(byte[]))
{
byte[] p = (byte[])o;
byte[] pNew = new byte[p.Length];
if(p.Length > 0) Array.Copy(p, pNew, p.Length);
o = pNew;
}
vdNew.m_d[kvp.Key] = o;
}
return vdNew;
}
public static byte[] Serialize(VariantDictionary p)
{
if(p == null) { Debug.Assert(false); return null; }
byte[] pbRet;
using(MemoryStream ms = new MemoryStream())
{
MemUtil.Write(ms, MemUtil.UInt16ToBytes(VdVersion));
foreach(KeyValuePair<string, object> kvp in p.m_d)
{
string strName = kvp.Key;
if(string.IsNullOrEmpty(strName)) { Debug.Assert(false); continue; }
byte[] pbName = StrUtil.Utf8.GetBytes(strName);
object o = kvp.Value;
if(o == null) { Debug.Assert(false); continue; }
Type t = o.GetType();
VdType vt = VdType.None;
byte[] pbValue = null;
if(t == typeof(uint))
{
vt = VdType.UInt32;
pbValue = MemUtil.UInt32ToBytes((uint)o);
}
else if(t == typeof(ulong))
{
vt = VdType.UInt64;
pbValue = MemUtil.UInt64ToBytes((ulong)o);
}
else if(t == typeof(bool))
{
vt = VdType.Bool;
pbValue = new byte[1];
pbValue[0] = ((bool)o ? (byte)1 : (byte)0);
}
else if(t == typeof(int))
{
vt = VdType.Int32;
pbValue = MemUtil.Int32ToBytes((int)o);
}
else if(t == typeof(long))
{
vt = VdType.Int64;
pbValue = MemUtil.Int64ToBytes((long)o);
}
else if(t == typeof(string))
{
vt = VdType.String;
pbValue = StrUtil.Utf8.GetBytes((string)o);
}
else if(t == typeof(byte[]))
{
vt = VdType.ByteArray;
pbValue = (byte[])o;
}
else { Debug.Assert(false); continue; } // Unknown type
ms.WriteByte((byte)vt);
MemUtil.Write(ms, MemUtil.Int32ToBytes(pbName.Length));
MemUtil.Write(ms, pbName);
MemUtil.Write(ms, MemUtil.Int32ToBytes(pbValue.Length));
MemUtil.Write(ms, pbValue);
}
ms.WriteByte((byte)VdType.None);
pbRet = ms.ToArray();
}
return pbRet;
}
public static VariantDictionary Deserialize(byte[] pb)
{
if(pb == null) { Debug.Assert(false); return null; }
VariantDictionary d = new VariantDictionary();
using(MemoryStream ms = new MemoryStream(pb, false))
{
ushort uVersion = MemUtil.BytesToUInt16(MemUtil.Read(ms, 2));
if((uVersion & VdmCritical) > (VdVersion & VdmCritical))
throw new FormatException(KLRes.FileNewVerReq);
while(true)
{
int iType = ms.ReadByte();
if(iType < 0) throw new EndOfStreamException(KLRes.FileCorrupted);
byte btType = (byte)iType;
if(btType == (byte)VdType.None) break;
int cbName = MemUtil.BytesToInt32(MemUtil.Read(ms, 4));
byte[] pbName = MemUtil.Read(ms, cbName);
if(pbName.Length != cbName)
throw new EndOfStreamException(KLRes.FileCorrupted);
string strName = StrUtil.Utf8.GetString(pbName, 0, pbName.Length);
int cbValue = MemUtil.BytesToInt32(MemUtil.Read(ms, 4));
byte[] pbValue = MemUtil.Read(ms, cbValue);
if(pbValue.Length != cbValue)
throw new EndOfStreamException(KLRes.FileCorrupted);
switch(btType)
{
case (byte)VdType.UInt32:
if(cbValue == 4)
d.SetUInt32(strName, MemUtil.BytesToUInt32(pbValue));
else { Debug.Assert(false); }
break;
case (byte)VdType.UInt64:
if(cbValue == 8)
d.SetUInt64(strName, MemUtil.BytesToUInt64(pbValue));
else { Debug.Assert(false); }
break;
case (byte)VdType.Bool:
if(cbValue == 1)
d.SetBool(strName, (pbValue[0] != 0));
else { Debug.Assert(false); }
break;
case (byte)VdType.Int32:
if(cbValue == 4)
d.SetInt32(strName, MemUtil.BytesToInt32(pbValue));
else { Debug.Assert(false); }
break;
case (byte)VdType.Int64:
if(cbValue == 8)
d.SetInt64(strName, MemUtil.BytesToInt64(pbValue));
else { Debug.Assert(false); }
break;
case (byte)VdType.String:
d.SetString(strName, StrUtil.Utf8.GetString(pbValue, 0, pbValue.Length));
break;
case (byte)VdType.ByteArray:
d.SetByteArray(strName, pbValue);
break;
default:
Debug.Assert(false); // Unknown type
break;
}
}
Debug.Assert(ms.ReadByte() < 0);
}
return d;
}
}
}

View File

@@ -0,0 +1,254 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2017 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.Collections.Generic;
using System.Diagnostics;
using System.IO;
using ModernKeePassLib.Resources;
using ModernKeePassLib.Utility;
namespace ModernKeePassLib.Cryptography.Cipher
{
/// <summary>
/// Implementation of the ChaCha20 cipher with a 96-bit nonce,
/// as specified in RFC 7539.
/// https://tools.ietf.org/html/rfc7539
/// </summary>
public sealed class ChaCha20Cipher : CtrBlockCipher
{
private uint[] m_s = new uint[16]; // State
private uint[] m_x = new uint[16]; // Working buffer
private bool m_bLargeCounter; // See constructor documentation
private static readonly uint[] g_sigma = new uint[4] {
0x61707865, 0x3320646E, 0x79622D32, 0x6B206574
};
private const string StrNameRfc = "ChaCha20 (RFC 7539)";
public override int BlockSize
{
get { return 64; }
}
public ChaCha20Cipher(byte[] pbKey32, byte[] pbIV12) :
this(pbKey32, pbIV12, false)
{
}
/// <summary>
/// Constructor.
/// </summary>
/// <param name="pbKey32">Key (32 bytes).</param>
/// <param name="pbIV12">Nonce (12 bytes).</param>
/// <param name="bLargeCounter">If <c>false</c>, the RFC 7539 version
/// of ChaCha20 is used. In this case, only 256 GB of data can be
/// encrypted securely (because the block counter is a 32-bit variable);
/// an attempt to encrypt more data throws an exception.
/// If <paramref name="bLargeCounter" /> is <c>true</c>, the 32-bit
/// counter overflows to another 32-bit variable (i.e. the counter
/// effectively is a 64-bit variable), like in the original ChaCha20
/// specification by D. J. Bernstein (which has a 64-bit counter and a
/// 64-bit nonce). To be compatible with this version, the 64-bit nonce
/// must be stored in the last 8 bytes of <paramref name="pbIV12" />
/// and the first 4 bytes must be 0.
/// If the IV was generated randomly, a 12-byte IV and a large counter
/// can be used to securely encrypt more than 256 GB of data (but note
/// this is incompatible with RFC 7539 and the original specification).</param>
public ChaCha20Cipher(byte[] pbKey32, byte[] pbIV12, bool bLargeCounter) :
base()
{
if(pbKey32 == null) throw new ArgumentNullException("pbKey32");
if(pbKey32.Length != 32) throw new ArgumentOutOfRangeException("pbKey32");
if(pbIV12 == null) throw new ArgumentNullException("pbIV12");
if(pbIV12.Length != 12) throw new ArgumentOutOfRangeException("pbIV12");
m_bLargeCounter = bLargeCounter;
// Key setup
m_s[4] = MemUtil.BytesToUInt32(pbKey32, 0);
m_s[5] = MemUtil.BytesToUInt32(pbKey32, 4);
m_s[6] = MemUtil.BytesToUInt32(pbKey32, 8);
m_s[7] = MemUtil.BytesToUInt32(pbKey32, 12);
m_s[8] = MemUtil.BytesToUInt32(pbKey32, 16);
m_s[9] = MemUtil.BytesToUInt32(pbKey32, 20);
m_s[10] = MemUtil.BytesToUInt32(pbKey32, 24);
m_s[11] = MemUtil.BytesToUInt32(pbKey32, 28);
m_s[0] = g_sigma[0];
m_s[1] = g_sigma[1];
m_s[2] = g_sigma[2];
m_s[3] = g_sigma[3];
// IV setup
m_s[12] = 0; // Counter
m_s[13] = MemUtil.BytesToUInt32(pbIV12, 0);
m_s[14] = MemUtil.BytesToUInt32(pbIV12, 4);
m_s[15] = MemUtil.BytesToUInt32(pbIV12, 8);
}
protected override void Dispose(bool bDisposing)
{
if(bDisposing)
{
MemUtil.ZeroArray<uint>(m_s);
MemUtil.ZeroArray<uint>(m_x);
}
base.Dispose(bDisposing);
}
protected override void NextBlock(byte[] pBlock)
{
if(pBlock == null) throw new ArgumentNullException("pBlock");
if(pBlock.Length != 64) throw new ArgumentOutOfRangeException("pBlock");
// x is a local alias for the working buffer; with this,
// the compiler/runtime might remove some checks
uint[] x = m_x;
if(x == null) throw new InvalidOperationException();
if(x.Length < 16) throw new InvalidOperationException();
uint[] s = m_s;
if(s == null) throw new InvalidOperationException();
if(s.Length < 16) throw new InvalidOperationException();
Array.Copy(s, x, 16);
unchecked
{
// 10 * 8 quarter rounds = 20 rounds
for(int i = 0; i < 10; ++i)
{
// Column quarter rounds
x[ 0] += x[ 4];
x[12] = MemUtil.RotateLeft32(x[12] ^ x[ 0], 16);
x[ 8] += x[12];
x[ 4] = MemUtil.RotateLeft32(x[ 4] ^ x[ 8], 12);
x[ 0] += x[ 4];
x[12] = MemUtil.RotateLeft32(x[12] ^ x[ 0], 8);
x[ 8] += x[12];
x[ 4] = MemUtil.RotateLeft32(x[ 4] ^ x[ 8], 7);
x[ 1] += x[ 5];
x[13] = MemUtil.RotateLeft32(x[13] ^ x[ 1], 16);
x[ 9] += x[13];
x[ 5] = MemUtil.RotateLeft32(x[ 5] ^ x[ 9], 12);
x[ 1] += x[ 5];
x[13] = MemUtil.RotateLeft32(x[13] ^ x[ 1], 8);
x[ 9] += x[13];
x[ 5] = MemUtil.RotateLeft32(x[ 5] ^ x[ 9], 7);
x[ 2] += x[ 6];
x[14] = MemUtil.RotateLeft32(x[14] ^ x[ 2], 16);
x[10] += x[14];
x[ 6] = MemUtil.RotateLeft32(x[ 6] ^ x[10], 12);
x[ 2] += x[ 6];
x[14] = MemUtil.RotateLeft32(x[14] ^ x[ 2], 8);
x[10] += x[14];
x[ 6] = MemUtil.RotateLeft32(x[ 6] ^ x[10], 7);
x[ 3] += x[ 7];
x[15] = MemUtil.RotateLeft32(x[15] ^ x[ 3], 16);
x[11] += x[15];
x[ 7] = MemUtil.RotateLeft32(x[ 7] ^ x[11], 12);
x[ 3] += x[ 7];
x[15] = MemUtil.RotateLeft32(x[15] ^ x[ 3], 8);
x[11] += x[15];
x[ 7] = MemUtil.RotateLeft32(x[ 7] ^ x[11], 7);
// Diagonal quarter rounds
x[ 0] += x[ 5];
x[15] = MemUtil.RotateLeft32(x[15] ^ x[ 0], 16);
x[10] += x[15];
x[ 5] = MemUtil.RotateLeft32(x[ 5] ^ x[10], 12);
x[ 0] += x[ 5];
x[15] = MemUtil.RotateLeft32(x[15] ^ x[ 0], 8);
x[10] += x[15];
x[ 5] = MemUtil.RotateLeft32(x[ 5] ^ x[10], 7);
x[ 1] += x[ 6];
x[12] = MemUtil.RotateLeft32(x[12] ^ x[ 1], 16);
x[11] += x[12];
x[ 6] = MemUtil.RotateLeft32(x[ 6] ^ x[11], 12);
x[ 1] += x[ 6];
x[12] = MemUtil.RotateLeft32(x[12] ^ x[ 1], 8);
x[11] += x[12];
x[ 6] = MemUtil.RotateLeft32(x[ 6] ^ x[11], 7);
x[ 2] += x[ 7];
x[13] = MemUtil.RotateLeft32(x[13] ^ x[ 2], 16);
x[ 8] += x[13];
x[ 7] = MemUtil.RotateLeft32(x[ 7] ^ x[ 8], 12);
x[ 2] += x[ 7];
x[13] = MemUtil.RotateLeft32(x[13] ^ x[ 2], 8);
x[ 8] += x[13];
x[ 7] = MemUtil.RotateLeft32(x[ 7] ^ x[ 8], 7);
x[ 3] += x[ 4];
x[14] = MemUtil.RotateLeft32(x[14] ^ x[ 3], 16);
x[ 9] += x[14];
x[ 4] = MemUtil.RotateLeft32(x[ 4] ^ x[ 9], 12);
x[ 3] += x[ 4];
x[14] = MemUtil.RotateLeft32(x[14] ^ x[ 3], 8);
x[ 9] += x[14];
x[ 4] = MemUtil.RotateLeft32(x[ 4] ^ x[ 9], 7);
}
for(int i = 0; i < 16; ++i) x[i] += s[i];
for(int i = 0; i < 16; ++i)
{
int i4 = i << 2;
uint xi = x[i];
pBlock[i4] = (byte)xi;
pBlock[i4 + 1] = (byte)(xi >> 8);
pBlock[i4 + 2] = (byte)(xi >> 16);
pBlock[i4 + 3] = (byte)(xi >> 24);
}
++s[12];
if(s[12] == 0)
{
if(!m_bLargeCounter)
throw new InvalidOperationException(
KLRes.EncDataTooLarge.Replace(@"{PARAM}", StrNameRfc));
++s[13]; // Increment high half of large counter
}
}
}
public long Seek(long lOffset, SeekOrigin so)
{
if(so != SeekOrigin.Begin) throw new NotSupportedException();
if((lOffset < 0) || ((lOffset & 63) != 0) ||
((lOffset >> 6) > (long)uint.MaxValue))
throw new ArgumentOutOfRangeException("lOffset");
m_s[12] = (uint)(lOffset >> 6);
InvalidateBlock();
return lOffset;
}
}
}

View File

@@ -0,0 +1,177 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2017 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.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;
using ModernKeePassLib.Resources;
namespace ModernKeePassLib.Cryptography.Cipher
{
public sealed class ChaCha20Engine : ICipherEngine2
{
private PwUuid m_uuid = new PwUuid(new byte[] {
0xD6, 0x03, 0x8A, 0x2B, 0x8B, 0x6F, 0x4C, 0xB5,
0xA5, 0x24, 0x33, 0x9A, 0x31, 0xDB, 0xB5, 0x9A
});
public PwUuid CipherUuid
{
get { return m_uuid; }
}
public string DisplayName
{
get
{
return ("ChaCha20 (" + KLRes.KeyBits.Replace(@"{PARAM}",
"256") + ", RFC 7539)");
}
}
public int KeyLength
{
get { return 32; }
}
public int IVLength
{
get { return 12; } // 96 bits
}
public Stream EncryptStream(Stream sPlainText, byte[] pbKey, byte[] pbIV)
{
return new ChaCha20Stream(sPlainText, true, pbKey, pbIV);
}
public Stream DecryptStream(Stream sEncrypted, byte[] pbKey, byte[] pbIV)
{
return new ChaCha20Stream(sEncrypted, false, pbKey, pbIV);
}
}
internal sealed class ChaCha20Stream : Stream
{
private Stream m_sBase;
private readonly bool m_bWriting;
private ChaCha20Cipher m_c;
private byte[] m_pbBuffer = null;
public override bool CanRead
{
get { return !m_bWriting; }
}
public override bool CanSeek
{
get { return false; }
}
public override bool CanWrite
{
get { return m_bWriting; }
}
public override long Length
{
get { Debug.Assert(false); throw new NotSupportedException(); }
}
public override long Position
{
get { Debug.Assert(false); throw new NotSupportedException(); }
set { Debug.Assert(false); throw new NotSupportedException(); }
}
public ChaCha20Stream(Stream sBase, bool bWriting, byte[] pbKey32,
byte[] pbIV12)
{
if(sBase == null) throw new ArgumentNullException("sBase");
m_sBase = sBase;
m_bWriting = bWriting;
m_c = new ChaCha20Cipher(pbKey32, pbIV12);
}
protected override void Dispose(bool bDisposing)
{
if(bDisposing)
{
if(m_sBase != null)
{
m_c.Dispose();
m_c = null;
//m_sBase.Close();
m_sBase = null;
}
m_pbBuffer = null;
}
base.Dispose(bDisposing);
}
public override void Flush()
{
Debug.Assert(m_sBase != null);
if(m_bWriting && (m_sBase != null)) m_sBase.Flush();
}
public override long Seek(long lOffset, SeekOrigin soOrigin)
{
Debug.Assert(false);
throw new NotImplementedException();
}
public override void SetLength(long lValue)
{
Debug.Assert(false);
throw new NotImplementedException();
}
public override int Read(byte[] pbBuffer, int iOffset, int nCount)
{
if(m_bWriting) throw new InvalidOperationException();
int cbRead = m_sBase.Read(pbBuffer, iOffset, nCount);
m_c.Decrypt(pbBuffer, iOffset, cbRead);
return cbRead;
}
public override void Write(byte[] pbBuffer, int iOffset, int nCount)
{
if(nCount < 0) throw new ArgumentOutOfRangeException("nCount");
if(nCount == 0) return;
if(!m_bWriting) throw new InvalidOperationException();
if((m_pbBuffer == null) || (m_pbBuffer.Length < nCount))
m_pbBuffer = new byte[nCount];
Array.Copy(pbBuffer, iOffset, m_pbBuffer, 0, nCount);
m_c.Encrypt(m_pbBuffer, 0, nCount);
m_sBase.Write(m_pbBuffer, 0, nCount);
}
}
}

View File

@@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2014 Dominik Reichl <dominik.reichl@t-online.de> Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -41,12 +41,17 @@ namespace ModernKeePassLib.Cryptography.Cipher
{ {
get get
{ {
if(m_poolGlobal != null) return m_poolGlobal; CipherPool cp = m_poolGlobal;
if(cp == null)
{
cp = new CipherPool();
cp.AddCipher(new StandardAesEngine());
cp.AddCipher(new ChaCha20Engine());
m_poolGlobal = new CipherPool(); m_poolGlobal = cp;
m_poolGlobal.AddCipher(new StandardAesEngine()); }
return m_poolGlobal; return cp;
} }
} }

View File

@@ -0,0 +1,109 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2017 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.Collections.Generic;
using System.Diagnostics;
using System.Text;
using ModernKeePassLib.Utility;
namespace ModernKeePassLib.Cryptography.Cipher
{
public abstract class CtrBlockCipher : IDisposable
{
private bool m_bDisposed = false;
private byte[] m_pBlock;
private int m_iBlockPos;
public abstract int BlockSize
{
get;
}
public CtrBlockCipher()
{
int cb = this.BlockSize;
if(cb <= 0) throw new InvalidOperationException("this.BlockSize");
m_pBlock = new byte[cb];
m_iBlockPos = cb;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool bDisposing)
{
if(bDisposing)
{
MemUtil.ZeroByteArray(m_pBlock);
m_iBlockPos = m_pBlock.Length;
m_bDisposed = true;
}
}
protected void InvalidateBlock()
{
m_iBlockPos = m_pBlock.Length;
}
protected abstract void NextBlock(byte[] pBlock);
public void Encrypt(byte[] m, int iOffset, int cb)
{
if(m_bDisposed) throw new ObjectDisposedException(null);
if(m == null) throw new ArgumentNullException("m");
if(iOffset < 0) throw new ArgumentOutOfRangeException("iOffset");
if(cb < 0) throw new ArgumentOutOfRangeException("cb");
if(iOffset > (m.Length - cb)) throw new ArgumentOutOfRangeException("cb");
int cbBlock = m_pBlock.Length;
while(cb > 0)
{
Debug.Assert(m_iBlockPos <= cbBlock);
if(m_iBlockPos == cbBlock)
{
NextBlock(m_pBlock);
m_iBlockPos = 0;
}
int cbCopy = Math.Min(cbBlock - m_iBlockPos, cb);
Debug.Assert(cbCopy > 0);
MemUtil.XorArray(m_pBlock, m_iBlockPos, m, iOffset, cbCopy);
m_iBlockPos += cbCopy;
iOffset += cbCopy;
cb -= cbCopy;
}
}
public void Decrypt(byte[] m, int iOffset, int cb)
{
Encrypt(m, iOffset, cb);
}
}
}

View File

@@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2014 Dominik Reichl <dominik.reichl@t-online.de> Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -63,4 +63,25 @@ namespace ModernKeePassLib.Cryptography.Cipher
/// <returns>Stream, from which the decrypted data can be read.</returns> /// <returns>Stream, from which the decrypted data can be read.</returns>
Stream DecryptStream(Stream sEncrypted, byte[] pbKey, byte[] pbIV); Stream DecryptStream(Stream sEncrypted, byte[] pbKey, byte[] pbIV);
} }
public interface ICipherEngine2 : ICipherEngine
{
/// <summary>
/// Length of an encryption key in bytes.
/// The base <c>ICipherEngine</c> assumes 32.
/// </summary>
int KeyLength
{
get;
}
/// <summary>
/// Length of the initialization vector in bytes.
/// The base <c>ICipherEngine</c> assumes 16.
/// </summary>
int IVLength
{
get;
}
}
} }

View File

@@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2014 Dominik Reichl <dominik.reichl@t-online.de> Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -17,182 +17,148 @@
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
// Implementation of the Salsa20 cipher, based on the eSTREAM submission. // Implementation of the Salsa20 cipher, based on the eSTREAM
// submission by D. J. Bernstein.
using System; using System;
using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using ModernKeePassLib.Utility; using ModernKeePassLib.Utility;
namespace ModernKeePassLib.Cryptography.Cipher namespace ModernKeePassLib.Cryptography.Cipher
{ {
public sealed class Salsa20Cipher : IDisposable public sealed class Salsa20Cipher : CtrBlockCipher
{ {
private uint[] m_state = new uint[16]; private uint[] m_s = new uint[16]; // State
private uint[] m_x = new uint[16]; // Working buffer private uint[] m_x = new uint[16]; // Working buffer
private byte[] m_output = new byte[64]; private static readonly uint[] g_sigma = new uint[4] {
private int m_outputPos = 64;
private static readonly uint[] m_sigma = new uint[4] {
0x61707865, 0x3320646E, 0x79622D32, 0x6B206574 0x61707865, 0x3320646E, 0x79622D32, 0x6B206574
}; };
public Salsa20Cipher(byte[] pbKey32, byte[] pbIV8) public override int BlockSize
{ {
KeySetup(pbKey32); get { return 64; }
IvSetup(pbIV8);
} }
~Salsa20Cipher() public Salsa20Cipher(byte[] pbKey32, byte[] pbIV8) : base()
{ {
Dispose(false); if(pbKey32 == null) throw new ArgumentNullException("pbKey32");
if(pbKey32.Length != 32) throw new ArgumentOutOfRangeException("pbKey32");
if(pbIV8 == null) throw new ArgumentNullException("pbIV8");
if(pbIV8.Length != 8) throw new ArgumentOutOfRangeException("pbIV8");
// Key setup
m_s[1] = MemUtil.BytesToUInt32(pbKey32, 0);
m_s[2] = MemUtil.BytesToUInt32(pbKey32, 4);
m_s[3] = MemUtil.BytesToUInt32(pbKey32, 8);
m_s[4] = MemUtil.BytesToUInt32(pbKey32, 12);
m_s[11] = MemUtil.BytesToUInt32(pbKey32, 16);
m_s[12] = MemUtil.BytesToUInt32(pbKey32, 20);
m_s[13] = MemUtil.BytesToUInt32(pbKey32, 24);
m_s[14] = MemUtil.BytesToUInt32(pbKey32, 28);
m_s[0] = g_sigma[0];
m_s[5] = g_sigma[1];
m_s[10] = g_sigma[2];
m_s[15] = g_sigma[3];
// IV setup
m_s[6] = MemUtil.BytesToUInt32(pbIV8, 0);
m_s[7] = MemUtil.BytesToUInt32(pbIV8, 4);
m_s[8] = 0; // Counter, low
m_s[9] = 0; // Counter, high
} }
public void Dispose() protected override void Dispose(bool bDisposing)
{ {
Dispose(true); if(bDisposing)
GC.SuppressFinalize(this); {
MemUtil.ZeroArray<uint>(m_s);
MemUtil.ZeroArray<uint>(m_x);
}
base.Dispose(bDisposing);
} }
private void Dispose(bool bDisposing) protected override void NextBlock(byte[] pBlock)
{ {
// Clear sensitive data if(pBlock == null) throw new ArgumentNullException("pBlock");
Array.Clear(m_state, 0, m_state.Length); if(pBlock.Length != 64) throw new ArgumentOutOfRangeException("pBlock");
Array.Clear(m_x, 0, m_x.Length);
}
private void NextOutput() // x is a local alias for the working buffer; with this,
{ // the compiler/runtime might remove some checks
uint[] x = m_x; // Local alias for working buffer uint[] x = m_x;
if(x == null) throw new InvalidOperationException();
// Compiler/runtime might remove array bound checks after this
if(x.Length < 16) throw new InvalidOperationException(); if(x.Length < 16) throw new InvalidOperationException();
Array.Copy(m_state, x, 16); uint[] s = m_s;
if(s == null) throw new InvalidOperationException();
if(s.Length < 16) throw new InvalidOperationException();
Array.Copy(s, x, 16);
unchecked unchecked
{ {
for(int i = 0; i < 10; ++i) // (int i = 20; i > 0; i -= 2) // 10 * 8 quarter rounds = 20 rounds
for(int i = 0; i < 10; ++i)
{ {
x[ 4] ^= Rotl32(x[ 0] + x[12], 7); x[ 4] ^= MemUtil.RotateLeft32(x[ 0] + x[12], 7);
x[ 8] ^= Rotl32(x[ 4] + x[ 0], 9); x[ 8] ^= MemUtil.RotateLeft32(x[ 4] + x[ 0], 9);
x[12] ^= Rotl32(x[ 8] + x[ 4], 13); x[12] ^= MemUtil.RotateLeft32(x[ 8] + x[ 4], 13);
x[ 0] ^= Rotl32(x[12] + x[ 8], 18); x[ 0] ^= MemUtil.RotateLeft32(x[12] + x[ 8], 18);
x[ 9] ^= Rotl32(x[ 5] + x[ 1], 7);
x[13] ^= Rotl32(x[ 9] + x[ 5], 9); x[ 9] ^= MemUtil.RotateLeft32(x[ 5] + x[ 1], 7);
x[ 1] ^= Rotl32(x[13] + x[ 9], 13); x[13] ^= MemUtil.RotateLeft32(x[ 9] + x[ 5], 9);
x[ 5] ^= Rotl32(x[ 1] + x[13], 18); x[ 1] ^= MemUtil.RotateLeft32(x[13] + x[ 9], 13);
x[14] ^= Rotl32(x[10] + x[ 6], 7); x[ 5] ^= MemUtil.RotateLeft32(x[ 1] + x[13], 18);
x[ 2] ^= Rotl32(x[14] + x[10], 9);
x[ 6] ^= Rotl32(x[ 2] + x[14], 13); x[14] ^= MemUtil.RotateLeft32(x[10] + x[ 6], 7);
x[10] ^= Rotl32(x[ 6] + x[ 2], 18); x[ 2] ^= MemUtil.RotateLeft32(x[14] + x[10], 9);
x[ 3] ^= Rotl32(x[15] + x[11], 7); x[ 6] ^= MemUtil.RotateLeft32(x[ 2] + x[14], 13);
x[ 7] ^= Rotl32(x[ 3] + x[15], 9); x[10] ^= MemUtil.RotateLeft32(x[ 6] + x[ 2], 18);
x[11] ^= Rotl32(x[ 7] + x[ 3], 13);
x[15] ^= Rotl32(x[11] + x[ 7], 18); x[ 3] ^= MemUtil.RotateLeft32(x[15] + x[11], 7);
x[ 1] ^= Rotl32(x[ 0] + x[ 3], 7); x[ 7] ^= MemUtil.RotateLeft32(x[ 3] + x[15], 9);
x[ 2] ^= Rotl32(x[ 1] + x[ 0], 9); x[11] ^= MemUtil.RotateLeft32(x[ 7] + x[ 3], 13);
x[ 3] ^= Rotl32(x[ 2] + x[ 1], 13); x[15] ^= MemUtil.RotateLeft32(x[11] + x[ 7], 18);
x[ 0] ^= Rotl32(x[ 3] + x[ 2], 18);
x[ 6] ^= Rotl32(x[ 5] + x[ 4], 7); x[ 1] ^= MemUtil.RotateLeft32(x[ 0] + x[ 3], 7);
x[ 7] ^= Rotl32(x[ 6] + x[ 5], 9); x[ 2] ^= MemUtil.RotateLeft32(x[ 1] + x[ 0], 9);
x[ 4] ^= Rotl32(x[ 7] + x[ 6], 13); x[ 3] ^= MemUtil.RotateLeft32(x[ 2] + x[ 1], 13);
x[ 5] ^= Rotl32(x[ 4] + x[ 7], 18); x[ 0] ^= MemUtil.RotateLeft32(x[ 3] + x[ 2], 18);
x[11] ^= Rotl32(x[10] + x[ 9], 7);
x[ 8] ^= Rotl32(x[11] + x[10], 9); x[ 6] ^= MemUtil.RotateLeft32(x[ 5] + x[ 4], 7);
x[ 9] ^= Rotl32(x[ 8] + x[11], 13); x[ 7] ^= MemUtil.RotateLeft32(x[ 6] + x[ 5], 9);
x[10] ^= Rotl32(x[ 9] + x[ 8], 18); x[ 4] ^= MemUtil.RotateLeft32(x[ 7] + x[ 6], 13);
x[12] ^= Rotl32(x[15] + x[14], 7); x[ 5] ^= MemUtil.RotateLeft32(x[ 4] + x[ 7], 18);
x[13] ^= Rotl32(x[12] + x[15], 9);
x[14] ^= Rotl32(x[13] + x[12], 13); x[11] ^= MemUtil.RotateLeft32(x[10] + x[ 9], 7);
x[15] ^= Rotl32(x[14] + x[13], 18); x[ 8] ^= MemUtil.RotateLeft32(x[11] + x[10], 9);
x[ 9] ^= MemUtil.RotateLeft32(x[ 8] + x[11], 13);
x[10] ^= MemUtil.RotateLeft32(x[ 9] + x[ 8], 18);
x[12] ^= MemUtil.RotateLeft32(x[15] + x[14], 7);
x[13] ^= MemUtil.RotateLeft32(x[12] + x[15], 9);
x[14] ^= MemUtil.RotateLeft32(x[13] + x[12], 13);
x[15] ^= MemUtil.RotateLeft32(x[14] + x[13], 18);
} }
for(int i = 0; i < 16; ++i) for(int i = 0; i < 16; ++i) x[i] += s[i];
x[i] += m_state[i];
for(int i = 0; i < 16; ++i) for(int i = 0; i < 16; ++i)
{ {
m_output[i << 2] = (byte)x[i]; int i4 = i << 2;
m_output[(i << 2) + 1] = (byte)(x[i] >> 8); uint xi = x[i];
m_output[(i << 2) + 2] = (byte)(x[i] >> 16);
m_output[(i << 2) + 3] = (byte)(x[i] >> 24); pBlock[i4] = (byte)xi;
pBlock[i4 + 1] = (byte)(xi >> 8);
pBlock[i4 + 2] = (byte)(xi >> 16);
pBlock[i4 + 3] = (byte)(xi >> 24);
} }
m_outputPos = 0; ++s[8];
++m_state[8]; if(s[8] == 0) ++s[9];
if(m_state[8] == 0) ++m_state[9];
}
}
private static uint Rotl32(uint x, int b)
{
unchecked
{
return ((x << b) | (x >> (32 - b)));
}
}
private static uint U8To32Little(byte[] pb, int iOffset)
{
unchecked
{
return ((uint)pb[iOffset] | ((uint)pb[iOffset + 1] << 8) |
((uint)pb[iOffset + 2] << 16) | ((uint)pb[iOffset + 3] << 24));
}
}
private void KeySetup(byte[] k)
{
if(k == null) throw new ArgumentNullException("k");
if(k.Length != 32) throw new ArgumentException();
m_state[1] = U8To32Little(k, 0);
m_state[2] = U8To32Little(k, 4);
m_state[3] = U8To32Little(k, 8);
m_state[4] = U8To32Little(k, 12);
m_state[11] = U8To32Little(k, 16);
m_state[12] = U8To32Little(k, 20);
m_state[13] = U8To32Little(k, 24);
m_state[14] = U8To32Little(k, 28);
m_state[0] = m_sigma[0];
m_state[5] = m_sigma[1];
m_state[10] = m_sigma[2];
m_state[15] = m_sigma[3];
}
private void IvSetup(byte[] pbIV)
{
if(pbIV == null) throw new ArgumentNullException("pbIV");
if(pbIV.Length != 8) throw new ArgumentException();
m_state[6] = U8To32Little(pbIV, 0);
m_state[7] = U8To32Little(pbIV, 4);
m_state[8] = 0;
m_state[9] = 0;
}
public void Encrypt(byte[] m, int nByteCount, bool bXor)
{
if(m == null) throw new ArgumentNullException("m");
if(nByteCount > m.Length) throw new ArgumentException();
int nBytesRem = nByteCount, nOffset = 0;
while(nBytesRem > 0)
{
Debug.Assert((m_outputPos >= 0) && (m_outputPos <= 64));
if(m_outputPos == 64) NextOutput();
Debug.Assert(m_outputPos < 64);
int nCopy = Math.Min(64 - m_outputPos, nBytesRem);
if(bXor) MemUtil.XorArray(m_output, m_outputPos, m, nOffset, nCopy);
else Array.Copy(m_output, m_outputPos, m, nOffset, nCopy);
m_outputPos += nCopy;
nBytesRem -= nCopy;
nOffset += nCopy;
} }
} }
} }

View File

@@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2014 Dominik Reichl <dominik.reichl@t-online.de> Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -39,12 +39,7 @@ namespace ModernKeePassLib.Cryptography.Cipher
/// </summary> /// </summary>
public sealed class StandardAesEngine : ICipherEngine public sealed class StandardAesEngine : ICipherEngine
{ {
#if !ModernKeePassLib && !KeePassRT private static PwUuid g_uuidAes = null;
private const CipherMode m_rCipherMode = CipherMode.CBC;
private const PaddingMode m_rCipherPadding = PaddingMode.PKCS7;
#endif
private static PwUuid m_uuidAes = null;
/// <summary> /// <summary>
/// UUID of the cipher engine. This ID uniquely identifies the /// UUID of the cipher engine. This ID uniquely identifies the
@@ -54,26 +49,38 @@ namespace ModernKeePassLib.Cryptography.Cipher
{ {
get get
{ {
if(m_uuidAes == null) PwUuid pu = g_uuidAes;
if(pu == null)
{ {
m_uuidAes = new PwUuid(new byte[]{ pu = new PwUuid(new byte[] {
0x31, 0xC1, 0xF2, 0xE6, 0xBF, 0x71, 0x43, 0x50, 0x31, 0xC1, 0xF2, 0xE6, 0xBF, 0x71, 0x43, 0x50,
0xBE, 0x58, 0x05, 0x21, 0x6A, 0xFC, 0x5A, 0xFF }); 0xBE, 0x58, 0x05, 0x21, 0x6A, 0xFC, 0x5A, 0xFF });
g_uuidAes = pu;
} }
return m_uuidAes; return pu;
} }
} }
/// <summary> /// <summary>
/// Get the UUID of this cipher engine as <c>PwUuid</c> object. /// Get the UUID of this cipher engine as <c>PwUuid</c> object.
/// </summary> /// </summary>
public PwUuid CipherUuid => StandardAesEngine.AesUuid; public PwUuid CipherUuid
{
get { return StandardAesEngine.AesUuid; }
}
/// <summary> /// <summary>
/// Get a displayable name describing this cipher engine. /// Get a displayable name describing this cipher engine.
/// </summary> /// </summary>
public string DisplayName { get { return KLRes.EncAlgorithmAes; } } public string DisplayName
{
get
{
return ("AES/Rijndael (" + KLRes.KeyBits.Replace(@"{PARAM}",
"256") + ", FIPS 197)");
}
}
private static void ValidateArguments(Stream stream, bool bEncrypt, byte[] pbKey, byte[] pbIV) private static void ValidateArguments(Stream stream, bool bEncrypt, byte[] pbKey, byte[] pbIV)
{ {
@@ -90,90 +97,24 @@ namespace ModernKeePassLib.Cryptography.Cipher
if(bEncrypt) if(bEncrypt)
{ {
Debug.Assert(stream.CanWrite); Debug.Assert(stream.CanWrite);
if(stream.CanWrite == false) throw new ArgumentException("Stream must be writable!"); if(!stream.CanWrite) throw new ArgumentException("Stream must be writable!");
} }
else // Decrypt else // Decrypt
{ {
Debug.Assert(stream.CanRead); Debug.Assert(stream.CanRead);
if(stream.CanRead == false) throw new ArgumentException("Encrypted stream must be readable!"); if(!stream.CanRead) throw new ArgumentException("Encrypted stream must be readable!");
} }
} }
private static Stream CreateStream(Stream s, bool bEncrypt, byte[] pbKey, byte[] pbIV) private static Stream CreateStream(Stream s, bool bEncrypt, byte[] pbKey, byte[] pbIV)
{ {
ValidateArguments(s, bEncrypt, pbKey, pbIV); StandardAesEngine.ValidateArguments(s, bEncrypt, pbKey, pbIV);
byte[] pbLocalIV = new byte[16]; byte[] pbLocalIV = new byte[16];
Array.Copy(pbIV, pbLocalIV, 16); Array.Copy(pbIV, pbLocalIV, 16);
byte[] pbLocalKey = new byte[32]; byte[] pbLocalKey = new byte[32];
Array.Copy(pbKey, pbLocalKey, 32); Array.Copy(pbKey, pbLocalKey, 32);
#if !ModernKeePassLib
//#if ModernKeePassLib
/*var provider = WinRTCrypto.SymmetricKeyAlgorithmProvider.
OpenAlgorithm(SymmetricAlgorithm.AesCbcPkcs7);
var key = provider.CreateSymmetricKey(pbLocalKey);
if (bEncrypt)
{
var encryptor = WinRTCrypto.CryptographicEngine.CreateEncryptor(
key, pbLocalIV);
return new CryptoStream(s, encryptor, CryptoStreamMode.Write);
} else
{
var decryptor = WinRTCrypto.CryptographicEngine.CreateDecryptor(
key, pbLocalIV);
return new CryptoStream(s, decryptor, CryptoStreamMode.Read);
}
*/
var provider = SymmetricKeyAlgorithmProvider.
OpenAlgorithm(SymmetricAlgorithmNames.AesCbcPkcs7);
var key = provider.CreateSymmetricKey(CryptographicBuffer.CreateFromByteArray(pbLocalKey));
using (var ms = new MemoryStream())
{
s.CopyTo(ms);
var data = CryptographicBuffer.CreateFromByteArray(ms.ToArray());
byte[] resultByteArray;
if (bEncrypt)
{
var encrypted = CryptographicEngine.Encrypt(key, data, CryptographicBuffer.CreateFromByteArray(pbLocalIV));
CryptographicBuffer.CopyToByteArray(encrypted, out resultByteArray);
return new MemoryStream(resultByteArray);
}
else
{
var decrypted = CryptographicEngine.Decrypt(key, data, CryptographicBuffer.CreateFromByteArray(pbLocalIV));
CryptographicBuffer.CopyToByteArray(decrypted, out resultByteArray);
return new MemoryStream(resultByteArray, true);
}
}
//#else
//#if !KeePassRT
//#if !ModernKeePassLib
RijndaelManaged r = new RijndaelManaged();
if(r.BlockSize != 128) // AES block size
{
Debug.Assert(false);
r.BlockSize = 128;
}
r.IV = pbLocalIV;
r.KeySize = 256;
r.Key = pbLocalKey;
r.Mode = m_rCipherMode;
r.Padding = m_rCipherPadding;
ICryptoTransform iTransform = (bEncrypt ? r.CreateEncryptor() : r.CreateDecryptor());
Debug.Assert(iTransform != null);
if(iTransform == null) throw new SecurityException("Unable to create Rijndael transform!");
return new CryptoStream(s, iTransform, bEncrypt ? CryptoStreamMode.Write :
CryptoStreamMode.Read);
#else
AesEngine aes = new AesEngine(); AesEngine aes = new AesEngine();
CbcBlockCipher cbc = new CbcBlockCipher(aes); CbcBlockCipher cbc = new CbcBlockCipher(aes);
PaddedBufferedBlockCipher bc = new PaddedBufferedBlockCipher(cbc, PaddedBufferedBlockCipher bc = new PaddedBufferedBlockCipher(cbc,
@@ -185,19 +126,16 @@ namespace ModernKeePassLib.Cryptography.Cipher
IBufferedCipher cpRead = (bEncrypt ? null : bc); IBufferedCipher cpRead = (bEncrypt ? null : bc);
IBufferedCipher cpWrite = (bEncrypt ? bc : null); IBufferedCipher cpWrite = (bEncrypt ? bc : null);
return new CipherStream(s, cpRead, cpWrite); return new CipherStream(s, cpRead, cpWrite);
#endif
//#endif
} }
public Stream EncryptStream(Stream sPlainText, byte[] pbKey, byte[] pbIV) public Stream EncryptStream(Stream sPlainText, byte[] pbKey, byte[] pbIV)
{ {
return CreateStream(sPlainText, true, pbKey, pbIV); return StandardAesEngine.CreateStream(sPlainText, true, pbKey, pbIV);
} }
public Stream DecryptStream(Stream sEncrypted, byte[] pbKey, byte[] pbIV) public Stream DecryptStream(Stream sEncrypted, byte[] pbKey, byte[] pbIV)
{ {
return CreateStream(sEncrypted, false, pbKey, pbIV); return StandardAesEngine.CreateStream(sEncrypted, false, pbKey, pbIV);
} }
} }
} }

View File

@@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2014 Dominik Reichl <dominik.reichl@t-online.de> Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -18,6 +18,7 @@
*/ */
using System; using System;
using System.Collections;
#if ModernKeePassLib #if ModernKeePassLib
using Windows.Security.Cryptography; using Windows.Security.Cryptography;
using ModernKeePassLib.Utility; using ModernKeePassLib.Utility;
@@ -27,37 +28,43 @@ using System.Security.Cryptography;
#endif #endif
using System.IO; using System.IO;
using System.Diagnostics; using System.Diagnostics;
using System.Globalization;
using ModernKeePassLib.Native;
namespace ModernKeePassLib.Cryptography namespace ModernKeePassLib.Cryptography
{ {
/// <summary> /// <summary>
/// Cryptographically strong random number generator. The returned values /// Cryptographically secure pseudo-random number generator.
/// are unpredictable and cannot be reproduced. /// The returned values are unpredictable and cannot be reproduced.
/// <c>CryptoRandom</c> is a singleton class. /// <c>CryptoRandom</c> is a singleton class.
/// </summary> /// </summary>
public sealed class CryptoRandom public sealed class CryptoRandom
{ {
private byte[] m_pbEntropyPool = new byte[64]; private byte[] m_pbEntropyPool = new byte[64];
private uint m_uCounter; private ulong m_uCounter;
#if ModernKeePassLib
//private IRandomNumberGenerator m_rng = NetFxCrypto.RandomNumberGenerator;
#else
private RNGCryptoServiceProvider m_rng = new RNGCryptoServiceProvider();
#endif
private ulong m_uGeneratedBytesCount = 0; private ulong m_uGeneratedBytesCount = 0;
private object m_oSyncRoot = new object(); private static readonly object g_oSyncRoot = new object();
private readonly object m_oSyncRoot = new object();
private static CryptoRandom m_pInstance = null; private static CryptoRandom g_pInstance = null;
public static CryptoRandom Instance public static CryptoRandom Instance
{ {
get get
{ {
if(m_pInstance != null) return m_pInstance; CryptoRandom cr;
lock(g_oSyncRoot)
{
cr = g_pInstance;
if(cr == null)
{
cr = new CryptoRandom();
g_pInstance = cr;
}
}
m_pInstance = new CryptoRandom(); return cr;
return m_pInstance;
} }
} }
@@ -84,10 +91,13 @@ namespace ModernKeePassLib.Cryptography
private CryptoRandom() private CryptoRandom()
{ {
Random r = new Random(); // Random rWeak = new Random(); // Based on tick count
m_uCounter = (uint)r.Next(); // byte[] pb = new byte[8];
// rWeak.NextBytes(pb);
// m_uCounter = MemUtil.BytesToUInt64(pb);
m_uCounter = (ulong)DateTime.UtcNow.ToBinary();
AddEntropy(GetSystemData(r)); AddEntropy(GetSystemData());
AddEntropy(GetCspData()); AddEntropy(GetCspData());
} }
@@ -103,64 +113,64 @@ namespace ModernKeePassLib.Cryptography
if(pbEntropy.Length == 0) { Debug.Assert(false); return; } if(pbEntropy.Length == 0) { Debug.Assert(false); return; }
byte[] pbNewData = pbEntropy; byte[] pbNewData = pbEntropy;
if(pbEntropy.Length >= 64) if(pbEntropy.Length > 64)
{ {
#if ModernKeePassLib #if ModernKeePassLib
/*var shaNew = WinRTCrypto.HashAlgorithmProvider.OpenAlgorithm(HashAlgorithm.Sha512);
pbNewData = shaNew.HashData(pbEntropy);*/
var sha256 = HashAlgorithmProvider.OpenAlgorithm(HashAlgorithmNames.Sha256); var sha256 = HashAlgorithmProvider.OpenAlgorithm(HashAlgorithmNames.Sha256);
var buffer = sha256.HashData(CryptographicBuffer.CreateFromByteArray(pbEntropy)); var buffer = sha256.HashData(CryptographicBuffer.CreateFromByteArray(pbEntropy));
CryptographicBuffer.CopyToByteArray(buffer, out pbNewData); CryptographicBuffer.CopyToByteArray(buffer, out pbNewData);
#else #else
#if KeePassLibSD
#if !KeePassLibSD using(SHA256Managed shaNew = new SHA256Managed())
SHA512Managed shaNew = new SHA512Managed();
#else #else
SHA256Managed shaNew = new SHA256Managed(); using(SHA512Managed shaNew = new SHA512Managed())
#endif #endif
pbNewData = shaNew.ComputeHash(pbEntropy); {
pbNewData = shaNew.ComputeHash(pbEntropy);
}
#endif #endif
} }
MemoryStream ms = new MemoryStream();
lock(m_oSyncRoot) lock(m_oSyncRoot)
{ {
ms.Write(m_pbEntropyPool, 0, m_pbEntropyPool.Length); int cbPool = m_pbEntropyPool.Length;
ms.Write(pbNewData, 0, pbNewData.Length); int cbNew = pbNewData.Length;
byte[] pbCmp = new byte[cbPool + cbNew];
Array.Copy(m_pbEntropyPool, pbCmp, cbPool);
Array.Copy(pbNewData, 0, pbCmp, cbPool, cbNew);
MemUtil.ZeroByteArray(m_pbEntropyPool);
byte[] pbFinal = ms.ToArray();
#if ModernKeePassLib #if ModernKeePassLib
/*var shaPool = WinRTCrypto.HashAlgorithmProvider.OpenAlgorithm(HashAlgorithm.Sha512);
m_pbEntropyPool = shaPool.HashData(pbFinal);*/
var sha256 = HashAlgorithmProvider.OpenAlgorithm(HashAlgorithmNames.Sha256); var sha256 = HashAlgorithmProvider.OpenAlgorithm(HashAlgorithmNames.Sha256);
var buffer = sha256.HashData(CryptographicBuffer.CreateFromByteArray(pbFinal)); var buffer = sha256.HashData(CryptographicBuffer.CreateFromByteArray(pbCmp));
CryptographicBuffer.CopyToByteArray(buffer, out m_pbEntropyPool); CryptographicBuffer.CopyToByteArray(buffer, out m_pbEntropyPool);
#else #else
#if KeePassLibSD
#if !KeePassLibSD using(SHA256Managed shaPool = new SHA256Managed())
Debug.Assert(pbFinal.Length == (64 + pbNewData.Length));
SHA512Managed shaPool = new SHA512Managed();
#else #else
SHA256Managed shaPool = new SHA256Managed(); using(SHA512Managed shaPool = new SHA512Managed())
#endif #endif
m_pbEntropyPool = shaPool.ComputeHash(pbFinal); {
m_pbEntropyPool = shaPool.ComputeHash(pbCmp);
}
#endif #endif
} MemUtil.ZeroByteArray(pbCmp);
ms.Dispose(); }
} }
private static byte[] GetSystemData(Random rWeak) private static byte[] GetSystemData()
{ {
MemoryStream ms = new MemoryStream(); MemoryStream ms = new MemoryStream();
byte[] pb; byte[] pb;
pb = MemUtil.UInt32ToBytes((uint)Environment.TickCount); pb = MemUtil.Int32ToBytes(Environment.TickCount);
ms.Write(pb, 0, pb.Length); MemUtil.Write(ms, pb);
pb = TimeUtil.PackTime(DateTime.Now); pb = MemUtil.Int64ToBytes(DateTime.UtcNow.ToBinary());
ms.Write(pb, 0, pb.Length); MemUtil.Write(ms, pb);
#if (!ModernKeePassLib && !KeePassLibSD && !KeePassRT) #if (!ModernKeePassLib && !KeePassLibSD && !KeePassRT)
// In try-catch for systems without GUI; // In try-catch for systems without GUI;
@@ -168,95 +178,138 @@ namespace ModernKeePassLib.Cryptography
try try
{ {
Point pt = Cursor.Position; Point pt = Cursor.Position;
pb = MemUtil.UInt32ToBytes((uint)pt.X); pb = MemUtil.Int32ToBytes(pt.X);
ms.Write(pb, 0, pb.Length); MemUtil.Write(ms, pb);
pb = MemUtil.UInt32ToBytes((uint)pt.Y); pb = MemUtil.Int32ToBytes(pt.Y);
ms.Write(pb, 0, pb.Length); MemUtil.Write(ms, pb);
} }
catch(Exception) { } catch(Exception) { }
#endif #endif
pb = MemUtil.UInt32ToBytes((uint)rWeak.Next());
ms.Write(pb, 0, pb.Length);
#if ModernKeePassLib #if ModernKeePassLib
pb = MemUtil.UInt32ToBytes((uint)Environment.ProcessorCount); pb = MemUtil.UInt32ToBytes((uint)Environment.ProcessorCount);
ms.Write(pb, 0, pb.Length); ms.Write(pb, 0, pb.Length);
pb = MemUtil.UInt32ToBytes((uint)Environment.CurrentManagedThreadId); pb = MemUtil.UInt32ToBytes((uint)Environment.CurrentManagedThreadId);
ms.Write(pb, 0, pb.Length); ms.Write(pb, 0, pb.Length);
#else #else
pb = MemUtil.UInt32ToBytes((uint)NativeLib.GetPlatformID()); pb = MemUtil.UInt32ToBytes((uint)NativeLib.GetPlatformID());
ms.Write(pb, 0, pb.Length); MemUtil.Write(ms, pb);
#endif
#if (!ModernKeePassLib && !KeePassLibSD && !KeePassRT) try
{
#if KeePassUAP
string strOS = EnvironmentExt.OSVersion.VersionString;
#else
string strOS = Environment.OSVersion.VersionString;
#endif
AddStrHash(ms, strOS);
pb = MemUtil.Int32ToBytes(Environment.ProcessorCount);
MemUtil.Write(ms, pb);
#if !KeePassUAP
AddStrHash(ms, Environment.CommandLine);
pb = MemUtil.Int64ToBytes(Environment.WorkingSet);
MemUtil.Write(ms, pb);
#endif
}
catch(Exception) { Debug.Assert(false); }
try
{
foreach(DictionaryEntry de in Environment.GetEnvironmentVariables())
{
AddStrHash(ms, (de.Key as string));
AddStrHash(ms, (de.Value as string));
}
}
catch(Exception) { Debug.Assert(false); }
#if KeePassUAP
pb = DiagnosticsExt.GetProcessEntropy();
MemUtil.Write(ms, pb);
#elif !KeePassLibSD
Process p = null; Process p = null;
try try
{ {
pb = MemUtil.UInt32ToBytes((uint)Environment.ProcessorCount);
ms.Write(pb, 0, pb.Length);
pb = MemUtil.UInt64ToBytes((ulong)Environment.WorkingSet);
ms.Write(pb, 0, pb.Length);
Version v = Environment.OSVersion.Version;
pb = MemUtil.UInt32ToBytes((uint)v.GetHashCode());
ms.Write(pb, 0, pb.Length);
p = Process.GetCurrentProcess(); p = Process.GetCurrentProcess();
pb = MemUtil.UInt64ToBytes((ulong)p.Handle.ToInt64()); pb = MemUtil.Int64ToBytes(p.Handle.ToInt64());
ms.Write(pb, 0, pb.Length); MemUtil.Write(ms, pb);
pb = MemUtil.UInt32ToBytes((uint)p.HandleCount); pb = MemUtil.Int32ToBytes(p.HandleCount);
ms.Write(pb, 0, pb.Length); MemUtil.Write(ms, pb);
pb = MemUtil.UInt32ToBytes((uint)p.Id); pb = MemUtil.Int32ToBytes(p.Id);
ms.Write(pb, 0, pb.Length); MemUtil.Write(ms, pb);
pb = MemUtil.UInt64ToBytes((ulong)p.NonpagedSystemMemorySize64); pb = MemUtil.Int64ToBytes(p.NonpagedSystemMemorySize64);
ms.Write(pb, 0, pb.Length); MemUtil.Write(ms, pb);
pb = MemUtil.UInt64ToBytes((ulong)p.PagedMemorySize64); pb = MemUtil.Int64ToBytes(p.PagedMemorySize64);
ms.Write(pb, 0, pb.Length); MemUtil.Write(ms, pb);
pb = MemUtil.UInt64ToBytes((ulong)p.PagedSystemMemorySize64); pb = MemUtil.Int64ToBytes(p.PagedSystemMemorySize64);
ms.Write(pb, 0, pb.Length); MemUtil.Write(ms, pb);
pb = MemUtil.UInt64ToBytes((ulong)p.PeakPagedMemorySize64); pb = MemUtil.Int64ToBytes(p.PeakPagedMemorySize64);
ms.Write(pb, 0, pb.Length); MemUtil.Write(ms, pb);
pb = MemUtil.UInt64ToBytes((ulong)p.PeakVirtualMemorySize64); pb = MemUtil.Int64ToBytes(p.PeakVirtualMemorySize64);
ms.Write(pb, 0, pb.Length); MemUtil.Write(ms, pb);
pb = MemUtil.UInt64ToBytes((ulong)p.PeakWorkingSet64); pb = MemUtil.Int64ToBytes(p.PeakWorkingSet64);
ms.Write(pb, 0, pb.Length); MemUtil.Write(ms, pb);
pb = MemUtil.UInt64ToBytes((ulong)p.PrivateMemorySize64); pb = MemUtil.Int64ToBytes(p.PrivateMemorySize64);
ms.Write(pb, 0, pb.Length); MemUtil.Write(ms, pb);
pb = MemUtil.UInt64ToBytes((ulong)p.StartTime.ToBinary()); pb = MemUtil.Int64ToBytes(p.StartTime.ToBinary());
ms.Write(pb, 0, pb.Length); MemUtil.Write(ms, pb);
pb = MemUtil.UInt64ToBytes((ulong)p.VirtualMemorySize64); pb = MemUtil.Int64ToBytes(p.VirtualMemorySize64);
ms.Write(pb, 0, pb.Length); MemUtil.Write(ms, pb);
pb = MemUtil.UInt64ToBytes((ulong)p.WorkingSet64); pb = MemUtil.Int64ToBytes(p.WorkingSet64);
ms.Write(pb, 0, pb.Length); MemUtil.Write(ms, pb);
// Not supported in Mono 1.2.6: // Not supported in Mono 1.2.6:
// pb = MemUtil.UInt32ToBytes((uint)p.SessionId); // pb = MemUtil.UInt32ToBytes((uint)p.SessionId);
// ms.Write(pb, 0, pb.Length); // MemUtil.Write(ms, pb);
} }
catch(Exception) { } catch(Exception) { Debug.Assert(NativeLib.IsUnix()); }
finally finally
{ {
try { if(p != null) p.Dispose(); } try { if(p != null) p.Dispose(); }
catch(Exception) { Debug.Assert(false); } catch(Exception) { Debug.Assert(false); }
} }
#endif #endif
#endif
try
{
CultureInfo ci = CultureInfo.CurrentCulture;
if(ci != null)
{
pb = MemUtil.Int32ToBytes(ci.GetHashCode());
MemUtil.Write(ms, pb);
}
else { Debug.Assert(false); }
}
catch(Exception) { Debug.Assert(false); }
pb = Guid.NewGuid().ToByteArray(); pb = Guid.NewGuid().ToByteArray();
ms.Write(pb, 0, pb.Length); MemUtil.Write(ms, pb);
byte[] pbAll = ms.ToArray(); byte[] pbAll = ms.ToArray();
ms.Dispose(); ms.Dispose();
return pbAll; return pbAll;
} }
private static void AddStrHash(Stream s, string str)
{
if(s == null) { Debug.Assert(false); return; }
if(string.IsNullOrEmpty(str)) return;
byte[] pbUtf8 = StrUtil.Utf8.GetBytes(str);
byte[] pbHash = CryptoUtil.HashSha256(pbUtf8);
MemUtil.Write(s, pbHash);
}
private byte[] GetCspData() private byte[] GetCspData()
{ {
byte[] pbCspRandom = new byte[32]; byte[] pbCspRandom = new byte[32];
//m_rng.GetBytes(pbCspRandom); //m_rng.GetBytes(pbCspRandom);
CryptographicBuffer.CopyToByteArray(CryptographicBuffer.GenerateRandom(32), out pbCspRandom); CryptographicBuffer.CopyToByteArray(CryptographicBuffer.GenerateRandom(32), out pbCspRandom);
return pbCspRandom; return pbCspRandom;
} }
@@ -265,39 +318,32 @@ namespace ModernKeePassLib.Cryptography
if(this.GenerateRandom256Pre != null) if(this.GenerateRandom256Pre != null)
this.GenerateRandom256Pre(this, EventArgs.Empty); this.GenerateRandom256Pre(this, EventArgs.Empty);
byte[] pbFinal; byte[] pbCmp;
lock(m_oSyncRoot) lock(m_oSyncRoot)
{ {
unchecked { m_uCounter += 386047; } // Prime number m_uCounter += 0x74D8B29E4D38E161UL; // Prime number
byte[] pbCounter = MemUtil.UInt32ToBytes(m_uCounter); byte[] pbCounter = MemUtil.UInt64ToBytes(m_uCounter);
byte[] pbCspRandom = GetCspData(); byte[] pbCspRandom = GetCspData();
MemoryStream ms = new MemoryStream(); int cbPool = m_pbEntropyPool.Length;
ms.Write(m_pbEntropyPool, 0, m_pbEntropyPool.Length); int cbCtr = pbCounter.Length;
ms.Write(pbCounter, 0, pbCounter.Length); int cbCsp = pbCspRandom.Length;
ms.Write(pbCspRandom, 0, pbCspRandom.Length);
pbFinal = ms.ToArray(); pbCmp = new byte[cbPool + cbCtr + cbCsp];
Debug.Assert(pbFinal.Length == (m_pbEntropyPool.Length + Array.Copy(m_pbEntropyPool, pbCmp, cbPool);
pbCounter.Length + pbCspRandom.Length)); Array.Copy(pbCounter, 0, pbCmp, cbPool, cbCtr);
ms.Dispose(); Array.Copy(pbCspRandom, 0, pbCmp, cbPool + cbCtr, cbCsp);
MemUtil.ZeroByteArray(pbCspRandom);
m_uGeneratedBytesCount += 32; m_uGeneratedBytesCount += 32;
} }
#if ModernKeePassLib byte[] pbRet = CryptoUtil.HashSha256(pbCmp);
/*var sha256 = WinRTCrypto.HashAlgorithmProvider.OpenAlgorithm(HashAlgorithm.Sha256); MemUtil.ZeroByteArray(pbCmp);
return sha256.HashData(pbFinal);*/ return pbRet;
var sha256 = HashAlgorithmProvider.OpenAlgorithm(HashAlgorithmNames.Sha256); }
var buffer = sha256.HashData(CryptographicBuffer.CreateFromByteArray(pbFinal));
byte[] result;
CryptographicBuffer.CopyToByteArray(buffer, out result);
return result;
#else
SHA256Managed sha256 = new SHA256Managed();
return sha256.ComputeHash(pbFinal);
#endif
}
/// <summary> /// <summary>
/// Get a number of cryptographically strong random bytes. /// Get a number of cryptographically strong random bytes.
@@ -308,30 +354,54 @@ namespace ModernKeePassLib.Cryptography
/// random bytes.</returns> /// random bytes.</returns>
public byte[] GetRandomBytes(uint uRequestedBytes) public byte[] GetRandomBytes(uint uRequestedBytes)
{ {
if(uRequestedBytes == 0) return new byte[0]; // Allow zero-length array if(uRequestedBytes == 0) return MemUtil.EmptyByteArray;
if(uRequestedBytes > (uint)int.MaxValue)
{
Debug.Assert(false);
throw new ArgumentOutOfRangeException("uRequestedBytes");
}
byte[] pbRes = new byte[uRequestedBytes]; int cbRem = (int)uRequestedBytes;
long lPos = 0; byte[] pbRes = new byte[cbRem];
int iPos = 0;
while(uRequestedBytes != 0) while(cbRem != 0)
{ {
byte[] pbRandom256 = GenerateRandom256(); byte[] pbRandom256 = GenerateRandom256();
Debug.Assert(pbRandom256.Length == 32); Debug.Assert(pbRandom256.Length == 32);
long lCopy = (long)((uRequestedBytes < 32) ? uRequestedBytes : 32); int cbCopy = Math.Min(cbRem, pbRandom256.Length);
Array.Copy(pbRandom256, 0, pbRes, iPos, cbCopy);
#if (!ModernKeePassLib && !KeePassLibSD && !KeePassRT) MemUtil.ZeroByteArray(pbRandom256);
Array.Copy(pbRandom256, 0, pbRes, lPos, lCopy);
#else
Array.Copy(pbRandom256, 0, pbRes, (int)lPos, (int)lCopy);
#endif
lPos += lCopy; iPos += cbCopy;
uRequestedBytes -= (uint)lCopy; cbRem -= cbCopy;
} }
Debug.Assert((int)lPos == pbRes.Length); Debug.Assert(iPos == pbRes.Length);
return pbRes; return pbRes;
} }
private static int g_iWeakSeed = 0;
public static Random NewWeakRandom()
{
long s64 = DateTime.UtcNow.ToBinary();
int s32 = (int)((s64 >> 32) ^ s64);
lock(g_oSyncRoot)
{
unchecked
{
g_iWeakSeed += 0x78A8C4B7; // Prime number
s32 ^= g_iWeakSeed;
}
}
// Prevent overflow in the Random constructor of .NET 2.0
if(s32 == int.MinValue) s32 = int.MaxValue;
return new Random(s32);
}
} }
} }

View File

@@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2014 Dominik Reichl <dominik.reichl@t-online.de> Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -19,14 +19,11 @@
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using Windows.Security.Cryptography.Core;
#if ModernKeePassLib
using Windows.Security.Cryptography;
#else
using System.Security.Cryptography;
#endif
using Windows.Security.Cryptography;
using Windows.Security.Cryptography.Core;
using ModernKeePassLib.Cryptography.Cipher; using ModernKeePassLib.Cryptography.Cipher;
using ModernKeePassLib.Utility;
namespace ModernKeePassLib.Cryptography namespace ModernKeePassLib.Cryptography
{ {
@@ -42,6 +39,7 @@ namespace ModernKeePassLib.Cryptography
/// <summary> /// <summary>
/// A variant of the ARCFour algorithm (RC4 incompatible). /// A variant of the ARCFour algorithm (RC4 incompatible).
/// Insecure; for backward compatibility only.
/// </summary> /// </summary>
ArcFourVariant = 1, ArcFourVariant = 1,
@@ -50,7 +48,12 @@ namespace ModernKeePassLib.Cryptography
/// </summary> /// </summary>
Salsa20 = 2, Salsa20 = 2,
Count = 3 /// <summary>
/// ChaCha20 stream cipher algorithm.
/// </summary>
ChaCha20 = 3,
Count = 4
} }
/// <summary> /// <summary>
@@ -59,47 +62,81 @@ namespace ModernKeePassLib.Cryptography
/// properties, but for the same seed always the same stream /// properties, but for the same seed always the same stream
/// is produced, i.e. this class can be used as stream cipher. /// is produced, i.e. this class can be used as stream cipher.
/// </summary> /// </summary>
public sealed class CryptoRandomStream public sealed class CryptoRandomStream : IDisposable
{ {
private CrsAlgorithm m_crsAlgorithm; private readonly CrsAlgorithm m_crsAlgorithm;
private bool m_bDisposed = false;
private byte[] m_pbState = null; private byte[] m_pbState = null;
private byte m_i = 0; private byte m_i = 0;
private byte m_j = 0; private byte m_j = 0;
private Salsa20Cipher m_salsa20 = null; private Salsa20Cipher m_salsa20 = null;
private ChaCha20Cipher m_chacha20 = null;
/// <summary> /// <summary>
/// Construct a new cryptographically secure random stream object. /// Construct a new cryptographically secure random stream object.
/// </summary> /// </summary>
/// <param name="genAlgorithm">Algorithm to use.</param> /// <param name="a">Algorithm to use.</param>
/// <param name="pbKey">Initialization key. Must not be <c>null</c> and /// <param name="pbKey">Initialization key. Must not be <c>null</c> and
/// must contain at least 1 byte.</param> /// must contain at least 1 byte.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the public CryptoRandomStream(CrsAlgorithm a, byte[] pbKey)
/// <paramref name="pbKey" /> parameter is <c>null</c>.</exception>
/// <exception cref="System.ArgumentException">Thrown if the
/// <paramref name="pbKey" /> parameter contains no bytes or the
/// algorithm is unknown.</exception>
public CryptoRandomStream(CrsAlgorithm genAlgorithm, byte[] pbKey)
{ {
m_crsAlgorithm = genAlgorithm; if(pbKey == null) { Debug.Assert(false); throw new ArgumentNullException("pbKey"); }
Debug.Assert(pbKey != null); if(pbKey == null) throw new ArgumentNullException("pbKey"); int cbKey = pbKey.Length;
if(cbKey <= 0)
{
Debug.Assert(false); // Need at least one byte
throw new ArgumentOutOfRangeException("pbKey");
}
uint uKeyLen = (uint)pbKey.Length; m_crsAlgorithm = a;
Debug.Assert(uKeyLen != 0); if(uKeyLen == 0) throw new ArgumentException();
if(genAlgorithm == CrsAlgorithm.ArcFourVariant) if(a == CrsAlgorithm.ChaCha20)
{
byte[] pbKey32 = new byte[32];
byte[] pbIV12 = new byte[12];
#if ModernKeePassLib
var sha256 = HashAlgorithmProvider.OpenAlgorithm(HashAlgorithmNames.Sha256);
var buffer = sha256.HashData(CryptographicBuffer.CreateFromByteArray(pbKey));
byte[] pbHash;
CryptographicBuffer.CopyToByteArray(buffer, out pbHash);
Array.Copy(pbHash, pbKey32, 32);
Array.Copy(pbHash, 32, pbIV12, 0, 12);
MemUtil.ZeroByteArray(pbHash);
#else
using(SHA512Managed h = new SHA512Managed())
{
byte[] pbHash = h.ComputeHash(pbKey);
Array.Copy(pbHash, pbKey32, 32);
Array.Copy(pbHash, 32, pbIV12, 0, 12);
MemUtil.ZeroByteArray(pbHash);
}
#endif
m_chacha20 = new ChaCha20Cipher(pbKey32, pbIV12, true);
}
else if(a == CrsAlgorithm.Salsa20)
{
byte[] pbKey32 = CryptoUtil.HashSha256(pbKey);
byte[] pbIV8 = new byte[8] { 0xE8, 0x30, 0x09, 0x4B,
0x97, 0x20, 0x5D, 0x2A }; // Unique constant
m_salsa20 = new Salsa20Cipher(pbKey32, pbIV8);
}
else if(a == CrsAlgorithm.ArcFourVariant)
{ {
// Fill the state linearly // Fill the state linearly
m_pbState = new byte[256]; m_pbState = new byte[256];
for(uint w = 0; w < 256; ++w) m_pbState[w] = (byte)w; for(int w = 0; w < 256; ++w) m_pbState[w] = (byte)w;
unchecked unchecked
{ {
byte j = 0, t; byte j = 0, t;
uint inxKey = 0; int inxKey = 0;
for(uint w = 0; w < 256; ++w) // Key setup for(int w = 0; w < 256; ++w) // Key setup
{ {
j += (byte)(m_pbState[w] + pbKey[inxKey]); j += (byte)(m_pbState[w] + pbKey[inxKey]);
@@ -108,34 +145,42 @@ namespace ModernKeePassLib.Cryptography
m_pbState[j] = t; m_pbState[j] = t;
++inxKey; ++inxKey;
if(inxKey >= uKeyLen) inxKey = 0; if(inxKey >= cbKey) inxKey = 0;
} }
} }
GetRandomBytes(512); // Increases security, see cryptanalysis GetRandomBytes(512); // Increases security, see cryptanalysis
} }
else if(genAlgorithm == CrsAlgorithm.Salsa20)
{
#if ModernKeePassLib
/*var sha256 = WinRTCrypto.HashAlgorithmProvider.OpenAlgorithm(HashAlgorithm.Sha256);
var pbKey32 = sha256.HashData(pbKey);*/
var sha256 = HashAlgorithmProvider.OpenAlgorithm(HashAlgorithmNames.Sha256);
var buffer = sha256.HashData(CryptographicBuffer.CreateFromByteArray(pbKey));
byte[] pbKey32;
CryptographicBuffer.CopyToByteArray(buffer, out pbKey32);
#else
SHA256Managed sha256 = new SHA256Managed();
byte[] pbKey32 = sha256.ComputeHash(pbKey);
#endif
byte[] pbIV = new byte[8] { 0xE8, 0x30, 0x09, 0x4B,
0x97, 0x20, 0x5D, 0x2A }; // Unique constant
m_salsa20 = new Salsa20Cipher(pbKey32, pbIV);
}
else // Unknown algorithm else // Unknown algorithm
{ {
Debug.Assert(false); Debug.Assert(false);
throw new ArgumentException(); throw new ArgumentOutOfRangeException("a");
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{
if(disposing)
{
if(m_crsAlgorithm == CrsAlgorithm.ChaCha20)
m_chacha20.Dispose();
else if(m_crsAlgorithm == CrsAlgorithm.Salsa20)
m_salsa20.Dispose();
else if(m_crsAlgorithm == CrsAlgorithm.ArcFourVariant)
{
MemUtil.ZeroByteArray(m_pbState);
m_i = 0;
m_j = 0;
}
else { Debug.Assert(false); }
m_bDisposed = true;
} }
} }
@@ -146,15 +191,24 @@ namespace ModernKeePassLib.Cryptography
/// <returns>Returns <paramref name="uRequestedCount" /> random bytes.</returns> /// <returns>Returns <paramref name="uRequestedCount" /> random bytes.</returns>
public byte[] GetRandomBytes(uint uRequestedCount) public byte[] GetRandomBytes(uint uRequestedCount)
{ {
if(uRequestedCount == 0) return new byte[0]; if(m_bDisposed) throw new ObjectDisposedException(null);
byte[] pbRet = new byte[uRequestedCount]; if(uRequestedCount == 0) return MemUtil.EmptyByteArray;
if(uRequestedCount > (uint)int.MaxValue)
throw new ArgumentOutOfRangeException("uRequestedCount");
int cb = (int)uRequestedCount;
if(m_crsAlgorithm == CrsAlgorithm.ArcFourVariant) byte[] pbRet = new byte[cb];
if(m_crsAlgorithm == CrsAlgorithm.ChaCha20)
m_chacha20.Encrypt(pbRet, 0, cb);
else if(m_crsAlgorithm == CrsAlgorithm.Salsa20)
m_salsa20.Encrypt(pbRet, 0, cb);
else if(m_crsAlgorithm == CrsAlgorithm.ArcFourVariant)
{ {
unchecked unchecked
{ {
for(uint w = 0; w < uRequestedCount; ++w) for(int w = 0; w < cb; ++w)
{ {
++m_i; ++m_i;
m_j += m_pbState[m_i]; m_j += m_pbState[m_i];
@@ -168,8 +222,6 @@ namespace ModernKeePassLib.Cryptography
} }
} }
} }
else if(m_crsAlgorithm == CrsAlgorithm.Salsa20)
m_salsa20.Encrypt(pbRet, pbRet.Length, false);
else { Debug.Assert(false); } else { Debug.Assert(false); }
return pbRet; return pbRet;
@@ -178,14 +230,7 @@ namespace ModernKeePassLib.Cryptography
public ulong GetRandomUInt64() public ulong GetRandomUInt64()
{ {
byte[] pb = GetRandomBytes(8); byte[] pb = GetRandomBytes(8);
return MemUtil.BytesToUInt64(pb);
unchecked
{
return ((ulong)pb[0]) | ((ulong)pb[1] << 8) |
((ulong)pb[2] << 16) | ((ulong)pb[3] << 24) |
((ulong)pb[4] << 32) | ((ulong)pb[5] << 40) |
((ulong)pb[6] << 48) | ((ulong)pb[7] << 56);
}
} }
#if CRSBENCHMARK #if CRSBENCHMARK
@@ -211,8 +256,10 @@ namespace ModernKeePassLib.Cryptography
int nStart = Environment.TickCount; int nStart = Environment.TickCount;
for(int i = 0; i < nRounds; ++i) for(int i = 0; i < nRounds; ++i)
{ {
CryptoRandomStream c = new CryptoRandomStream(cra, pbKey); using(CryptoRandomStream c = new CryptoRandomStream(cra, pbKey))
c.GetRandomBytes((uint)nDataSize); {
c.GetRandomBytes((uint)nDataSize);
}
} }
int nEnd = Environment.TickCount; int nEnd = Environment.TickCount;

View File

@@ -0,0 +1,191 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2017 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.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Text;
using Windows.Security.Cryptography;
using Windows.Security.Cryptography.Core;
using ModernKeePassLib.Native;
using ModernKeePassLib.Utility;
namespace ModernKeePassLib.Cryptography
{
public static class CryptoUtil
{
public static byte[] HashSha256(byte[] pbData)
{
if(pbData == null) throw new ArgumentNullException("pbData");
return HashSha256(pbData, 0, pbData.Length);
}
public static byte[] HashSha256(byte[] pbData, int iOffset, int cbCount)
{
if(pbData == null) throw new ArgumentNullException("pbData");
#if DEBUG
byte[] pbCopy = new byte[pbData.Length];
Array.Copy(pbData, pbCopy, pbData.Length);
#endif
byte[] pbHash;
#if ModernKeePassLib
var h = HashAlgorithmProvider.OpenAlgorithm(HashAlgorithmNames.Sha256).CreateHash();
CryptographicBuffer.CopyToByteArray(h.GetValueAndReset(), out pbHash);
#else
using(SHA256Managed h = new SHA256Managed())
{
pbHash = h.ComputeHash(pbData, iOffset, cbCount);
}
#endif
#if DEBUG
// Ensure the data has not been modified
Debug.Assert(MemUtil.ArraysEqual(pbData, pbCopy));
Debug.Assert((pbHash != null) && (pbHash.Length == 32));
byte[] pbZero = new byte[32];
Debug.Assert(!MemUtil.ArraysEqual(pbHash, pbZero));
#endif
return pbHash;
}
/// <summary>
/// Create a cryptographic key of length <paramref name="cbOut" />
/// (in bytes) from <paramref name="pbIn" />.
/// </summary>
public static byte[] ResizeKey(byte[] pbIn, int iInOffset,
int cbIn, int cbOut)
{
if(pbIn == null) throw new ArgumentNullException("pbIn");
if(cbOut < 0) throw new ArgumentOutOfRangeException("cbOut");
if(cbOut == 0) return MemUtil.EmptyByteArray;
byte[] pbHash;
if(cbOut <= 32) pbHash = HashSha256(pbIn, iInOffset, cbIn);
else
{
#if ModernKeePassLib
var h = HashAlgorithmProvider.OpenAlgorithm(HashAlgorithmNames.Sha512).CreateHash();
CryptographicBuffer.CopyToByteArray(h.GetValueAndReset(), out pbHash);
#else
using(SHA512Managed h = new SHA512Managed())
{
pbHash = h.ComputeHash(pbIn, iInOffset, cbIn);
}
#endif
}
if(cbOut == pbHash.Length) return pbHash;
byte[] pbRet = new byte[cbOut];
if(cbOut < pbHash.Length)
Array.Copy(pbHash, pbRet, cbOut);
else
{
int iPos = 0;
ulong r = 0;
while(iPos < cbOut)
{
Debug.Assert(pbHash.Length == 64);
byte[] pbR = MemUtil.UInt64ToBytes(r);
#if ModernKeePassLib
var h = MacAlgorithmProvider.OpenAlgorithm(MacAlgorithmNames.HmacSha256).CreateHash(CryptographicBuffer.CreateFromByteArray(pbR));
byte[] pbPart;
CryptographicBuffer.CopyToByteArray(h.GetValueAndReset(), out pbPart);
int cbCopy = Math.Min(cbOut - iPos, pbPart.Length);
Debug.Assert(cbCopy > 0);
Array.Copy(pbPart, 0, pbRet, iPos, cbCopy);
iPos += cbCopy;
++r;
MemUtil.ZeroByteArray(pbPart);
#else
using (HMACSHA256 h = new HMACSHA256(pbHash))
{
byte[] pbR = MemUtil.UInt64ToBytes(r);
byte[] pbPart = h.ComputeHash(pbR);
int cbCopy = Math.Min(cbOut - iPos, pbPart.Length);
Debug.Assert(cbCopy > 0);
Array.Copy(pbPart, 0, pbRet, iPos, cbCopy);
iPos += cbCopy;
++r;
MemUtil.ZeroByteArray(pbPart);
}
#endif
}
Debug.Assert(iPos == cbOut);
}
#if DEBUG
byte[] pbZero = new byte[pbHash.Length];
Debug.Assert(!MemUtil.ArraysEqual(pbHash, pbZero));
#endif
MemUtil.ZeroByteArray(pbHash);
return pbRet;
}
#if !ModernKeePassLib
private static bool? g_obAesCsp = null;
internal static SymmetricAlgorithm CreateAes()
{
if(g_obAesCsp.HasValue)
return (g_obAesCsp.Value ? CreateAesCsp() : new RijndaelManaged());
SymmetricAlgorithm a = CreateAesCsp();
g_obAesCsp = (a != null);
return (a ?? new RijndaelManaged());
}
private static SymmetricAlgorithm CreateAesCsp()
{
try
{
// On Windows, the CSP implementation is only minimally
// faster (and for key derivations it's not used anyway,
// as KeePass uses a native implementation based on
// CNG/BCrypt, which is much faster)
if(!NativeLib.IsUnix()) return null;
string strFqn = Assembly.CreateQualifiedName(
"System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089",
"System.Security.Cryptography.AesCryptoServiceProvider");
Type t = Type.GetType(strFqn);
if(t == null) return null;
return (Activator.CreateInstance(t) as SymmetricAlgorithm);
}
catch(Exception) { Debug.Assert(false); }
return null;
}
#endif
}
}

View File

@@ -0,0 +1,280 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2017 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
*/
// This implementation is based on the official reference C
// implementation by Samuel Neves (CC0 1.0 Universal).
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using ModernKeePassLib.Utility;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Tls;
namespace ModernKeePassLib.Cryptography.Hash
{
public sealed class Blake2b : IDigest
{
private const int NbRounds = 12;
private const int NbBlockBytes = 128;
private const int NbMaxOutBytes = 64;
private static readonly ulong[] g_vIV = new ulong[8] {
0x6A09E667F3BCC908UL, 0xBB67AE8584CAA73BUL,
0x3C6EF372FE94F82BUL, 0xA54FF53A5F1D36F1UL,
0x510E527FADE682D1UL, 0x9B05688C2B3E6C1FUL,
0x1F83D9ABFB41BD6BUL, 0x5BE0CD19137E2179UL
};
private static readonly int[] g_vSigma = new int[NbRounds * 16] {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3,
11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4,
7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8,
9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13,
2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9,
12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11,
13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10,
6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5,
10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3
};
private readonly int m_cbHashLength;
private ulong[] m_h = new ulong[8];
private ulong[] m_t = new ulong[2];
private ulong[] m_f = new ulong[2];
private byte[] m_buf = new byte[NbBlockBytes];
private int m_cbBuf = 0;
private ulong[] m_m = new ulong[16];
private ulong[] m_v = new ulong[16];
public string AlgorithmName { get; } = "Blake2b";
public int HashSize { get; internal set; }
public Blake2b()
{
m_cbHashLength = NbMaxOutBytes;
this.HashSize = NbMaxOutBytes * 8; // Bits
Initialize();
}
public Blake2b(int cbHashLength)
{
if((cbHashLength < 0) || (cbHashLength > NbMaxOutBytes))
throw new ArgumentOutOfRangeException("cbHashLength");
m_cbHashLength = cbHashLength;
this.HashSize = cbHashLength * 8; // Bits
Initialize();
}
public void Initialize()
{
Debug.Assert(m_h.Length == g_vIV.Length);
Array.Copy(g_vIV, m_h, m_h.Length);
// Fan-out = 1, depth = 1
m_h[0] ^= 0x0000000001010000UL ^ (ulong)m_cbHashLength;
Array.Clear(m_t, 0, m_t.Length);
Array.Clear(m_f, 0, m_f.Length);
Array.Clear(m_buf, 0, m_buf.Length);
m_cbBuf = 0;
Array.Clear(m_m, 0, m_m.Length);
Array.Clear(m_v, 0, m_v.Length);
}
private static void G(ulong[] v, ulong[] m, int r16, int i,
int a, int b, int c, int d)
{
int p = r16 + i;
v[a] += v[b] + m[g_vSigma[p]];
v[d] = MemUtil.RotateRight64(v[d] ^ v[a], 32);
v[c] += v[d];
v[b] = MemUtil.RotateRight64(v[b] ^ v[c], 24);
v[a] += v[b] + m[g_vSigma[p + 1]];
v[d] = MemUtil.RotateRight64(v[d] ^ v[a], 16);
v[c] += v[d];
v[b] = MemUtil.RotateRight64(v[b] ^ v[c], 63);
}
private void Compress(byte[] pb, int iOffset)
{
ulong[] v = m_v;
ulong[] m = m_m;
ulong[] h = m_h;
for(int i = 0; i < 16; ++i)
m[i] = MemUtil.BytesToUInt64(pb, iOffset + (i << 3));
Array.Copy(h, v, 8);
v[8] = g_vIV[0];
v[9] = g_vIV[1];
v[10] = g_vIV[2];
v[11] = g_vIV[3];
v[12] = g_vIV[4] ^ m_t[0];
v[13] = g_vIV[5] ^ m_t[1];
v[14] = g_vIV[6] ^ m_f[0];
v[15] = g_vIV[7] ^ m_f[1];
for(int r = 0; r < NbRounds; ++r)
{
int r16 = r << 4;
G(v, m, r16, 0, 0, 4, 8, 12);
G(v, m, r16, 2, 1, 5, 9, 13);
G(v, m, r16, 4, 2, 6, 10, 14);
G(v, m, r16, 6, 3, 7, 11, 15);
G(v, m, r16, 8, 0, 5, 10, 15);
G(v, m, r16, 10, 1, 6, 11, 12);
G(v, m, r16, 12, 2, 7, 8, 13);
G(v, m, r16, 14, 3, 4, 9, 14);
}
for(int i = 0; i < 8; ++i)
h[i] ^= v[i] ^ v[i + 8];
}
private void IncrementCounter(ulong cb)
{
m_t[0] += cb;
if(m_t[0] < cb) ++m_t[1];
}
public int GetDigestSize()
{
return HashSize;
}
public int GetByteLength()
{
return m_buf.Length;
}
public void Update(byte input)
{
throw new NotImplementedException();
}
public void BlockUpdate(byte[] array, int ibStart, int cbSize)
{
Debug.Assert(m_f[0] == 0);
if ((m_cbBuf + cbSize) > NbBlockBytes) // Not '>=' (buffer must not be empty)
{
int cbFill = NbBlockBytes - m_cbBuf;
if (cbFill > 0) Array.Copy(array, ibStart, m_buf, m_cbBuf, cbFill);
IncrementCounter((ulong)NbBlockBytes);
Compress(m_buf, 0);
m_cbBuf = 0;
cbSize -= cbFill;
ibStart += cbFill;
while (cbSize > NbBlockBytes) // Not '>=' (buffer must not be empty)
{
IncrementCounter((ulong)NbBlockBytes);
Compress(array, ibStart);
cbSize -= NbBlockBytes;
ibStart += NbBlockBytes;
}
}
if (cbSize > 0)
{
Debug.Assert((m_cbBuf + cbSize) <= NbBlockBytes);
Array.Copy(array, ibStart, m_buf, m_cbBuf, cbSize);
m_cbBuf += cbSize;
}
}
public int DoFinal(byte[] output, int outOff)
{
if (m_f[0] != 0) { Debug.Assert(false); throw new InvalidOperationException(); }
Debug.Assert(((m_t[1] == 0) && (m_t[0] == 0)) ||
(m_cbBuf > 0)); // Buffer must not be empty for last block processing
m_f[0] = ulong.MaxValue; // Indicate last block
int cbFill = NbBlockBytes - m_cbBuf;
if (cbFill > 0) Array.Clear(m_buf, m_cbBuf, cbFill);
IncrementCounter((ulong)m_cbBuf);
Compress(m_buf, 0);
byte[] pbHash = new byte[NbMaxOutBytes];
for (int i = 0; i < m_h.Length; ++i)
MemUtil.UInt64ToBytesEx(m_h[i], pbHash, i << 3);
if (m_cbHashLength == NbMaxOutBytes)
{
output = pbHash;
return output.Length;
}
Debug.Assert(m_cbHashLength < NbMaxOutBytes);
byte[] pbShort = new byte[m_cbHashLength];
if (m_cbHashLength > 0)
Array.Copy(pbHash, pbShort, m_cbHashLength);
MemUtil.ZeroByteArray(pbHash);
output = pbShort;
return output.Length;
}
public void Reset()
{
MemUtil.ZeroByteArray(m_buf);
}
public void TransformBlock(byte[] pbBuf, int p1, int pbBufLength, byte[] p3, int p4)
{
BlockUpdate(pbBuf, p1, pbBufLength);
}
public void TransformFinalBlock(byte[] emptyByteArray, int i, int i1)
{
DoFinal(emptyByteArray, i);
}
public void Clear()
{
Reset();
}
internal byte[] ComputeHash(byte[] pbOutBuffer)
{
byte[] result = new byte[pbOutBuffer.Length];
DoFinal(result, 0);
return result;
}
}
}

View File

@@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2014 Dominik Reichl <dominik.reichl@t-online.de> Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -19,7 +19,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text; using System.Diagnostics;
using System.IO; using System.IO;
#if ModernKeePassLib #if ModernKeePassLib
using Windows.Security.Cryptography; using Windows.Security.Cryptography;
@@ -29,7 +29,6 @@ using Org.BouncyCastle.Crypto.Digests;
#else #else
using System.Security.Cryptography; using System.Security.Cryptography;
#endif #endif
using System.Diagnostics;
using System.Runtime.InteropServices.ComTypes; using System.Runtime.InteropServices.ComTypes;
using ModernKeePassLib.Utility; using ModernKeePassLib.Utility;
using Org.BouncyCastle.Crypto.Tls; using Org.BouncyCastle.Crypto.Tls;
@@ -38,12 +37,12 @@ namespace ModernKeePassLib.Cryptography
{ {
public sealed class HashingStreamEx : Stream public sealed class HashingStreamEx : Stream
{ {
private Stream m_sBaseStream; private readonly Stream m_sBaseStream;
private bool m_bWriting; private readonly bool m_bWriting;
#if ModernKeePassLib #if ModernKeePassLib
//private ICryptoTransform m_hash; //private ICryptoTransform m_hash;
//private CryptographicHash m_hash; //private CryptographicHash m_hash;
private IDigest m_hash; private IDigest m_hash;
#else #else
private HashAlgorithm m_hash; private HashAlgorithm m_hash;
#endif #endif
@@ -78,7 +77,7 @@ namespace ModernKeePassLib.Cryptography
public override long Position public override long Position
{ {
get { return m_sBaseStream.Position; } get { return m_sBaseStream.Position; }
set { throw new NotSupportedException(); } set { Debug.Assert(false); throw new NotSupportedException(); }
} }
#if ModernKeePassLib #if ModernKeePassLib
@@ -88,8 +87,7 @@ namespace ModernKeePassLib.Cryptography
public HashingStreamEx(Stream sBaseStream, bool bWriting, HashAlgorithm hashAlgorithm) public HashingStreamEx(Stream sBaseStream, bool bWriting, HashAlgorithm hashAlgorithm)
#endif #endif
{ {
if(sBaseStream == null) if (sBaseStream == null) throw new ArgumentNullException("sBaseStream");
throw new ArgumentNullException("sBaseStream");
m_sBaseStream = sBaseStream; m_sBaseStream = sBaseStream;
m_bWriting = bWriting; m_bWriting = bWriting;
@@ -107,53 +105,51 @@ namespace ModernKeePassLib.Cryptography
try { if(m_hash == null) m_hash = HashAlgorithm.Create(); } try { if(m_hash == null) m_hash = HashAlgorithm.Create(); }
catch(Exception) { } catch(Exception) { }
#endif #endif
if (m_hash == null) { Debug.Assert(false); return; } if(m_hash == null) { Debug.Assert(false); return; }
// Validate hash algorithm // Validate hash algorithm
/*if((!m_hash.CanReuseTransform) || (!m_hash.CanTransformMultipleBlocks) || /*if(!m_hash.CanReuseTransform || !m_hash.CanTransformMultipleBlocks)
(m_hash.InputBlockSize != 1) || (m_hash.OutputBlockSize != 1))
{ {
#if false && DEBUG Debug.Assert(false);
MessageService.ShowWarning("Broken HashAlgorithm object in HashingStreamEx.");
#endif
m_hash = null; m_hash = null;
}*/ }*/
} }
public override void Flush()
{
m_sBaseStream.Flush();
}
#if ModernKeePassLib || KeePassRT #if ModernKeePassLib || KeePassRT
protected override void Dispose(bool disposing) protected override void Dispose(bool disposing)
{ {
if(!disposing) return; if (!disposing) return;
#else #else
public override void Close() public override void Close()
{ {
#endif #endif
if(m_hash != null) if (m_hash != null)
{ {
try try
{ {
//m_hash.TransformFinalBlock(new byte[0], 0, 0); //m_hash.TransformFinalBlock(new byte[0], 0, 0);
#if ModernKeePassLib #if ModernKeePassLib
//m_pbFinalHash = (m_hash as CryptographicHash).GetValueAndReset (); //m_pbFinalHash = (m_hash as CryptographicHash).GetValueAndReset ();
//CryptographicBuffer.CopyToByteArray(m_hash.GetValueAndReset(), out m_pbFinalHash); //CryptographicBuffer.CopyToByteArray(m_hash.GetValueAndReset(), out m_pbFinalHash);
m_pbFinalHash = new byte[32]; m_pbFinalHash = new byte[32];
m_hash.DoFinal(m_pbFinalHash, 0); m_hash.DoFinal(m_pbFinalHash, 0);
m_hash.Reset(); m_hash.Reset();
#else #else
m_pbFinalHash = m_hash.Hash; m_pbFinalHash = m_hash.Hash;
#endif #endif
} }
catch(Exception) { Debug.Assert(false); } catch (Exception)
{
Debug.Assert(false);
}
m_hash = null; base.Dispose(disposing);
} }
}
m_sBaseStream.Dispose(); public override void Flush()
{
m_sBaseStream.Flush();
} }
public override long Seek(long lOffset, SeekOrigin soOrigin) public override long Seek(long lOffset, SeekOrigin soOrigin)
@@ -185,12 +181,11 @@ namespace ModernKeePassLib.Cryptography
#endif #endif
if((m_hash != null) && (nRead > 0)) if((m_hash != null) && (nRead > 0))
//m_hash.TransformBlock(pbBuffer, nOffset, nRead, pbBuffer, nOffset); //m_hash.TransformBlock(pbBuffer, nOffset, nRead, pbBuffer, nOffset);
//m_hash.Append(CryptographicBuffer.CreateFromByteArray(pbBuffer)); m_hash.BlockUpdate(pbBuffer, nOffset, nRead);
m_hash.BlockUpdate(pbBuffer, nOffset, nRead);
#if DEBUG #if DEBUG
Debug.Assert(MemUtil.ArraysEqual(pbBuffer, pbOrg)); Debug.Assert(MemUtil.ArraysEqual(pbBuffer, pbOrg));
#endif #endif
return nRead; return nRead;
@@ -205,12 +200,12 @@ namespace ModernKeePassLib.Cryptography
Array.Copy(pbBuffer, pbOrg, pbBuffer.Length); Array.Copy(pbBuffer, pbOrg, pbBuffer.Length);
#endif #endif
if ((m_hash != null) && (nCount > 0)) if((m_hash != null) && (nCount > 0))
//m_hash.TransformBlock(pbBuffer, nOffset, nCount, pbBuffer, nOffset); //m_hash.TransformBlock(pbBuffer, nOffset, nCount, pbBuffer, nOffset);
//m_hash.Append(CryptographicBuffer.CreateFromByteArray(pbBuffer)); m_hash.BlockUpdate(pbBuffer, nOffset, nCount);
m_hash.BlockUpdate(pbBuffer, nOffset, nCount);
#if DEBUG #if DEBUG
Debug.Assert(MemUtil.ArraysEqual(pbBuffer, pbOrg)); Debug.Assert(MemUtil.ArraysEqual(pbBuffer, pbOrg));
#endif #endif
m_sBaseStream.Write(pbBuffer, nOffset, nCount); m_sBaseStream.Write(pbBuffer, nOffset, nCount);

View File

@@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2014 Dominik Reichl <dominik.reichl@t-online.de> Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -45,22 +45,14 @@ namespace ModernKeePassLib.Cryptography
uint uCodeDigits, bool bAddChecksum, int iTruncationOffset) uint uCodeDigits, bool bAddChecksum, int iTruncationOffset)
{ {
byte[] pbText = MemUtil.UInt64ToBytes(uFactor); byte[] pbText = MemUtil.UInt64ToBytes(uFactor);
Array.Reverse(pbText); // Big-Endian Array.Reverse(pbText); // To big-endian
#if ModernKeePassLib
/*var hsha1 = WinRTCrypto.MacAlgorithmProvider.OpenAlgorithm(MacAlgorithm.HmacSha1).CreateHash(pbSecret);
hsha1.Append(pbText);
var pbHash = hsha1.GetValueAndReset();*/
var hsha1 = MacAlgorithmProvider.OpenAlgorithm(MacAlgorithmNames.HmacSha1).CreateHash(CryptographicBuffer.CreateFromByteArray(pbSecret)); var hsha1 = MacAlgorithmProvider.OpenAlgorithm(MacAlgorithmNames.HmacSha1).CreateHash(CryptographicBuffer.CreateFromByteArray(pbSecret));
hsha1.Append(CryptographicBuffer.CreateFromByteArray(pbText)); hsha1.Append(CryptographicBuffer.CreateFromByteArray(pbText));
byte[] pbHash; byte[] pbHash;
CryptographicBuffer.CopyToByteArray(hsha1.GetValueAndReset(), out pbHash); CryptographicBuffer.CopyToByteArray(hsha1.GetValueAndReset(), out pbHash);
#else
HMACSHA1 hsha1 = new HMACSHA1(pbSecret);
byte[] pbHash = hsha1.ComputeHash(pbText);
#endif
uint uOffset = (uint)(pbHash[pbHash.Length - 1] & 0xF); uint uOffset = (uint)(pbHash[pbHash.Length - 1] & 0xF);
if((iTruncationOffset >= 0) && (iTruncationOffset < (pbHash.Length - 4))) if((iTruncationOffset >= 0) && (iTruncationOffset < (pbHash.Length - 4)))
uOffset = (uint)iTruncationOffset; uOffset = (uint)iTruncationOffset;

View File

@@ -0,0 +1,402 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2017 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.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using ModernKeePassLib.Native;
using ModernKeePassLib.Utility;
namespace ModernKeePassLib.Cryptography.KeyDerivation
{
public sealed partial class AesKdf : KdfEngine
{
private static bool TransformKeyGCrypt(byte[] pbData32, byte[] pbSeed32,
ulong uRounds)
{
byte[] pbNewData32 = null;
try
{
if(GCryptInitLib())
{
pbNewData32 = new byte[32];
Array.Copy(pbData32, pbNewData32, 32);
if(TransformKeyGCryptPriv(pbNewData32, pbSeed32, uRounds))
{
Array.Copy(pbNewData32, pbData32, 32);
return true;
}
}
}
catch(Exception) { }
finally { if(pbNewData32 != null) MemUtil.ZeroByteArray(pbNewData32); }
return false;
}
private static bool TransformKeyBenchmarkGCrypt(uint uTimeMs, out ulong uRounds)
{
uRounds = 0;
try
{
if(GCryptInitLib())
return TransformKeyBenchmarkGCryptPriv(uTimeMs, ref uRounds);
}
catch(Exception) { }
return false;
}
private static bool GCryptInitLib()
{
Debug.Assert(Marshal.SizeOf(typeof(int)) == 4); // Also on 64-bit systems
Debug.Assert(Marshal.SizeOf(typeof(uint)) == 4);
if(!NativeLib.IsUnix()) return false; // Independent of workaround state
if(!MonoWorkarounds.IsRequired(1468)) return false; // Can be turned off
// gcry_check_version initializes the library;
// throws when LibGCrypt is not available
NativeMethods.gcry_check_version(IntPtr.Zero);
return true;
}
// =============================================================
// Multi-threaded implementation
// For some reason, the following multi-threaded implementation
// is slower than the single-threaded implementation below
// (threading overhead by Mono? LibGCrypt threading issues?)
/* private sealed class GCryptTransformInfo : IDisposable
{
public IntPtr Data16;
public IntPtr Seed32;
public ulong Rounds;
public uint TimeMs;
public bool Success = false;
public GCryptTransformInfo(byte[] pbData32, int iDataOffset,
byte[] pbSeed32, ulong uRounds, uint uTimeMs)
{
this.Data16 = Marshal.AllocCoTaskMem(16);
Marshal.Copy(pbData32, iDataOffset, this.Data16, 16);
this.Seed32 = Marshal.AllocCoTaskMem(32);
Marshal.Copy(pbSeed32, 0, this.Seed32, 32);
this.Rounds = uRounds;
this.TimeMs = uTimeMs;
}
public void Dispose()
{
if(this.Data16 != IntPtr.Zero)
{
Marshal.WriteInt64(this.Data16, 0);
Marshal.WriteInt64(this.Data16, 8, 0);
Marshal.FreeCoTaskMem(this.Data16);
this.Data16 = IntPtr.Zero;
}
if(this.Seed32 != IntPtr.Zero)
{
Marshal.FreeCoTaskMem(this.Seed32);
this.Seed32 = IntPtr.Zero;
}
}
}
private static GCryptTransformInfo[] GCryptRun(byte[] pbData32,
byte[] pbSeed32, ulong uRounds, uint uTimeMs, ParameterizedThreadStart fL,
ParameterizedThreadStart fR)
{
GCryptTransformInfo tiL = new GCryptTransformInfo(pbData32, 0,
pbSeed32, uRounds, uTimeMs);
GCryptTransformInfo tiR = new GCryptTransformInfo(pbData32, 16,
pbSeed32, uRounds, uTimeMs);
Thread th = new Thread(fL);
th.Start(tiL);
fR(tiR);
th.Join();
Marshal.Copy(tiL.Data16, pbData32, 0, 16);
Marshal.Copy(tiR.Data16, pbData32, 16, 16);
tiL.Dispose();
tiR.Dispose();
if(tiL.Success && tiR.Success)
return new GCryptTransformInfo[2] { tiL, tiR };
return null;
}
private static bool TransformKeyGCryptPriv(byte[] pbData32, byte[] pbSeed32,
ulong uRounds)
{
return (GCryptRun(pbData32, pbSeed32, uRounds, 0,
new ParameterizedThreadStart(AesKdf.GCryptTransformTh),
new ParameterizedThreadStart(AesKdf.GCryptTransformTh)) != null);
}
private static bool GCryptInitCipher(ref IntPtr h, GCryptTransformInfo ti)
{
NativeMethods.gcry_cipher_open(ref h, NativeMethods.GCRY_CIPHER_AES256,
NativeMethods.GCRY_CIPHER_MODE_ECB, 0);
if(h == IntPtr.Zero) { Debug.Assert(false); return false; }
IntPtr n32 = new IntPtr(32);
if(NativeMethods.gcry_cipher_setkey(h, ti.Seed32, n32) != 0)
{
Debug.Assert(false);
return false;
}
return true;
}
private static void GCryptTransformTh(object o)
{
IntPtr h = IntPtr.Zero;
try
{
GCryptTransformInfo ti = (o as GCryptTransformInfo);
if(ti == null) { Debug.Assert(false); return; }
if(!GCryptInitCipher(ref h, ti)) return;
IntPtr n16 = new IntPtr(16);
for(ulong u = 0; u < ti.Rounds; ++u)
{
if(NativeMethods.gcry_cipher_encrypt(h, ti.Data16, n16,
IntPtr.Zero, IntPtr.Zero) != 0)
{
Debug.Assert(false);
return;
}
}
ti.Success = true;
}
catch(Exception) { Debug.Assert(false); }
finally
{
try { if(h != IntPtr.Zero) NativeMethods.gcry_cipher_close(h); }
catch(Exception) { Debug.Assert(false); }
}
}
private static bool TransformKeyBenchmarkGCryptPriv(uint uTimeMs, ref ulong uRounds)
{
GCryptTransformInfo[] v = GCryptRun(new byte[32], new byte[32],
0, uTimeMs,
new ParameterizedThreadStart(AesKdf.GCryptBenchmarkTh),
new ParameterizedThreadStart(AesKdf.GCryptBenchmarkTh));
if(v != null)
{
ulong uL = Math.Min(v[0].Rounds, ulong.MaxValue >> 1);
ulong uR = Math.Min(v[1].Rounds, ulong.MaxValue >> 1);
uRounds = (uL + uR) / 2;
return true;
}
return false;
}
private static void GCryptBenchmarkTh(object o)
{
IntPtr h = IntPtr.Zero;
try
{
GCryptTransformInfo ti = (o as GCryptTransformInfo);
if(ti == null) { Debug.Assert(false); return; }
if(!GCryptInitCipher(ref h, ti)) return;
ulong r = 0;
IntPtr n16 = new IntPtr(16);
int tStart = Environment.TickCount;
while(true)
{
for(ulong j = 0; j < BenchStep; ++j)
{
if(NativeMethods.gcry_cipher_encrypt(h, ti.Data16, n16,
IntPtr.Zero, IntPtr.Zero) != 0)
{
Debug.Assert(false);
return;
}
}
r += BenchStep;
if(r < BenchStep) // Overflow check
{
r = ulong.MaxValue;
break;
}
uint tElapsed = (uint)(Environment.TickCount - tStart);
if(tElapsed > ti.TimeMs) break;
}
ti.Rounds = r;
ti.Success = true;
}
catch(Exception) { Debug.Assert(false); }
finally
{
try { if(h != IntPtr.Zero) NativeMethods.gcry_cipher_close(h); }
catch(Exception) { Debug.Assert(false); }
}
} */
// =============================================================
// Single-threaded implementation
private static bool GCryptInitCipher(ref IntPtr h, IntPtr pSeed32)
{
NativeMethods.gcry_cipher_open(ref h, NativeMethods.GCRY_CIPHER_AES256,
NativeMethods.GCRY_CIPHER_MODE_ECB, 0);
if(h == IntPtr.Zero) { Debug.Assert(false); return false; }
IntPtr n32 = new IntPtr(32);
if(NativeMethods.gcry_cipher_setkey(h, pSeed32, n32) != 0)
{
Debug.Assert(false);
return false;
}
return true;
}
private static bool GCryptBegin(byte[] pbData32, byte[] pbSeed32,
ref IntPtr h, ref IntPtr pData32, ref IntPtr pSeed32)
{
pData32 = Marshal.AllocCoTaskMem(32);
pSeed32 = Marshal.AllocCoTaskMem(32);
Marshal.Copy(pbData32, 0, pData32, 32);
Marshal.Copy(pbSeed32, 0, pSeed32, 32);
return GCryptInitCipher(ref h, pSeed32);
}
private static void GCryptEnd(IntPtr h, IntPtr pData32, IntPtr pSeed32)
{
NativeMethods.gcry_cipher_close(h);
Marshal.WriteInt64(pData32, 0);
Marshal.WriteInt64(pData32, 8, 0);
Marshal.WriteInt64(pData32, 16, 0);
Marshal.WriteInt64(pData32, 24, 0);
Marshal.FreeCoTaskMem(pData32);
Marshal.FreeCoTaskMem(pSeed32);
}
private static bool TransformKeyGCryptPriv(byte[] pbData32, byte[] pbSeed32,
ulong uRounds)
{
IntPtr h = IntPtr.Zero, pData32 = IntPtr.Zero, pSeed32 = IntPtr.Zero;
if(!GCryptBegin(pbData32, pbSeed32, ref h, ref pData32, ref pSeed32))
return false;
try
{
IntPtr n32 = new IntPtr(32);
for(ulong i = 0; i < uRounds; ++i)
{
if(NativeMethods.gcry_cipher_encrypt(h, pData32, n32,
IntPtr.Zero, IntPtr.Zero) != 0)
{
Debug.Assert(false);
return false;
}
}
Marshal.Copy(pData32, pbData32, 0, 32);
return true;
}
catch(Exception) { Debug.Assert(false); }
finally { GCryptEnd(h, pData32, pSeed32); }
return false;
}
private static bool TransformKeyBenchmarkGCryptPriv(uint uTimeMs, ref ulong uRounds)
{
byte[] pbData32 = new byte[32];
byte[] pbSeed32 = new byte[32];
IntPtr h = IntPtr.Zero, pData32 = IntPtr.Zero, pSeed32 = IntPtr.Zero;
if(!GCryptBegin(pbData32, pbSeed32, ref h, ref pData32, ref pSeed32))
return false;
uint uMaxMs = uTimeMs;
ulong uDiv = 1;
if(uMaxMs <= (uint.MaxValue >> 1)) { uMaxMs *= 2U; uDiv = 2; }
try
{
ulong r = 0;
IntPtr n32 = new IntPtr(32);
int tStart = Environment.TickCount;
while(true)
{
for(ulong j = 0; j < BenchStep; ++j)
{
if(NativeMethods.gcry_cipher_encrypt(h, pData32, n32,
IntPtr.Zero, IntPtr.Zero) != 0)
{
Debug.Assert(false);
return false;
}
}
r += BenchStep;
if(r < BenchStep) // Overflow check
{
r = ulong.MaxValue;
break;
}
uint tElapsed = (uint)(Environment.TickCount - tStart);
if(tElapsed > uMaxMs) break;
}
uRounds = r / uDiv;
return true;
}
catch(Exception) { Debug.Assert(false); }
finally { GCryptEnd(h, pData32, pSeed32); }
return false;
}
}
}

View File

@@ -0,0 +1,206 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2017 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.Collections.Generic;
using System.Diagnostics;
using System.Text;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Parameters;
using ModernKeePassLib.Cryptography;
using ModernKeePassLib.Native;
using ModernKeePassLib.Utility;
namespace ModernKeePassLib.Cryptography.KeyDerivation
{
public sealed partial class AesKdf : KdfEngine
{
private static readonly PwUuid g_uuid = new PwUuid(new byte[] {
0xC9, 0xD9, 0xF3, 0x9A, 0x62, 0x8A, 0x44, 0x60,
0xBF, 0x74, 0x0D, 0x08, 0xC1, 0x8A, 0x4F, 0xEA });
public const string ParamRounds = "R"; // UInt64
public const string ParamSeed = "S"; // Byte[32]
private const ulong BenchStep = 3001;
public override PwUuid Uuid
{
get { return g_uuid; }
}
public override string Name
{
get { return "AES-KDF"; }
}
public AesKdf()
{
}
public override KdfParameters GetDefaultParameters()
{
KdfParameters p = base.GetDefaultParameters();
p.SetUInt64(ParamRounds, PwDefs.DefaultKeyEncryptionRounds);
return p;
}
public override void Randomize(KdfParameters p)
{
if(p == null) { Debug.Assert(false); return; }
Debug.Assert(g_uuid.Equals(p.KdfUuid));
byte[] pbSeed = CryptoRandom.Instance.GetRandomBytes(32);
p.SetByteArray(ParamSeed, pbSeed);
}
public override byte[] Transform(byte[] pbMsg, KdfParameters p)
{
if(pbMsg == null) throw new ArgumentNullException("pbMsg");
if(p == null) throw new ArgumentNullException("p");
Type tRounds = p.GetTypeOf(ParamRounds);
if(tRounds == null) throw new ArgumentNullException("p.Rounds");
if(tRounds != typeof(ulong)) throw new ArgumentOutOfRangeException("p.Rounds");
ulong uRounds = p.GetUInt64(ParamRounds, 0);
byte[] pbSeed = p.GetByteArray(ParamSeed);
if(pbSeed == null) throw new ArgumentNullException("p.Seed");
if(pbMsg.Length != 32)
{
Debug.Assert(false);
pbMsg = CryptoUtil.HashSha256(pbMsg);
}
if(pbSeed.Length != 32)
{
Debug.Assert(false);
pbSeed = CryptoUtil.HashSha256(pbSeed);
}
return TransformKey(pbMsg, pbSeed, uRounds);
}
private static byte[] TransformKey(byte[] pbOriginalKey32, byte[] pbKeySeed32,
ulong uNumRounds)
{
Debug.Assert((pbOriginalKey32 != null) && (pbOriginalKey32.Length == 32));
if(pbOriginalKey32 == null) throw new ArgumentNullException("pbOriginalKey32");
if(pbOriginalKey32.Length != 32) throw new ArgumentException();
Debug.Assert((pbKeySeed32 != null) && (pbKeySeed32.Length == 32));
if(pbKeySeed32 == null) throw new ArgumentNullException("pbKeySeed32");
if(pbKeySeed32.Length != 32) throw new ArgumentException();
byte[] pbNewKey = new byte[32];
Array.Copy(pbOriginalKey32, pbNewKey, pbNewKey.Length);
try
{
// Try to use the native library first
/*if(NativeLib.TransformKey256(pbNewKey, pbKeySeed32, uNumRounds))
return CryptoUtil.HashSha256(pbNewKey);*/
if(TransformKeyGCrypt(pbNewKey, pbKeySeed32, uNumRounds))
return CryptoUtil.HashSha256(pbNewKey);
if(TransformKeyManaged(pbNewKey, pbKeySeed32, uNumRounds))
return CryptoUtil.HashSha256(pbNewKey);
}
finally { MemUtil.ZeroByteArray(pbNewKey); }
return null;
}
internal static bool TransformKeyManaged(byte[] pbNewKey32, byte[] pbKeySeed32,
ulong uNumRounds)
{
KeyParameter kp = new KeyParameter(pbKeySeed32);
AesEngine aes = new AesEngine();
aes.Init(true, kp);
for(ulong i = 0; i < uNumRounds; ++i)
{
aes.ProcessBlock(pbNewKey32, 0, pbNewKey32, 0);
aes.ProcessBlock(pbNewKey32, 16, pbNewKey32, 16);
}
return true;
}
public override KdfParameters GetBestParameters(uint uMilliseconds)
{
KdfParameters p = GetDefaultParameters();
ulong uRounds;
// Try native method
/*if(NativeLib.TransformKeyBenchmark256(uMilliseconds, out uRounds))
{
p.SetUInt64(ParamRounds, uRounds);
return p;
}*/
if(TransformKeyBenchmarkGCrypt(uMilliseconds, out uRounds))
{
p.SetUInt64(ParamRounds, uRounds);
return p;
}
byte[] pbKey = new byte[32];
byte[] pbNewKey = new byte[32];
for(int i = 0; i < pbKey.Length; ++i)
{
pbKey[i] = (byte)i;
pbNewKey[i] = (byte)i;
}
KeyParameter kp = new KeyParameter(pbKey);
AesEngine aes = new AesEngine();
aes.Init(true, kp);
uRounds = 0;
int tStart = Environment.TickCount;
while(true)
{
for(ulong j = 0; j < BenchStep; ++j)
{
aes.ProcessBlock(pbNewKey, 0, pbNewKey, 0);
aes.ProcessBlock(pbNewKey, 16, pbNewKey, 16);
}
uRounds += BenchStep;
if(uRounds < BenchStep) // Overflow check
{
uRounds = ulong.MaxValue;
break;
}
uint tElapsed = (uint)(Environment.TickCount - tStart);
if(tElapsed > uMilliseconds) break;
}
p.SetUInt64(ParamRounds, uRounds);
return p;
}
}
}

View File

@@ -0,0 +1,636 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2017 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
*/
// This implementation is based on the official reference C
// implementation by Daniel Dinu and Dmitry Khovratovich (CC0 1.0).
// Relative iterations (* = B2ROUND_ARRAYS \\ G_INLINED):
// * | false true
// ------+-----------
// false | 8885 9618
// true | 9009 9636
#define ARGON2_B2ROUND_ARRAYS
#define ARGON2_G_INLINED
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using ModernKeePassLib.Cryptography.Hash;
using ModernKeePassLib.Utility;
namespace ModernKeePassLib.Cryptography.KeyDerivation
{
public sealed partial class Argon2Kdf : KdfEngine
{
private const ulong NbBlockSize = 1024;
private const ulong NbBlockSizeInQW = NbBlockSize / 8UL;
private const ulong NbSyncPoints = 4;
private const int NbPreHashDigestLength = 64;
private const int NbPreHashSeedLength = NbPreHashDigestLength + 8;
#if ARGON2_B2ROUND_ARRAYS
private static int[][] g_vFBCols = null;
private static int[][] g_vFBRows = null;
#endif
private sealed class Argon2Ctx
{
public uint Version = 0;
public ulong Lanes = 0;
public ulong TCost = 0;
public ulong MCost = 0;
public ulong MemoryBlocks = 0;
public ulong SegmentLength = 0;
public ulong LaneLength = 0;
public ulong[] Mem = null;
}
private sealed class Argon2ThreadInfo
{
public Argon2Ctx Context = null;
public ManualResetEvent Finished = new ManualResetEvent(false);
public ulong Pass = 0;
public ulong Lane = 0;
public ulong Slice = 0;
public ulong Index = 0;
public void Release()
{
if(this.Finished != null)
{
this.Finished.Dispose();
this.Finished = null;
}
else { Debug.Assert(false); }
}
}
private static byte[] Argon2d(byte[] pbMsg, byte[] pbSalt, uint uParallel,
ulong uMem, ulong uIt, int cbOut, uint uVersion, byte[] pbSecretKey,
byte[] pbAssocData)
{
pbSecretKey = (pbSecretKey ?? MemUtil.EmptyByteArray);
pbAssocData = (pbAssocData ?? MemUtil.EmptyByteArray);
#if ARGON2_B2ROUND_ARRAYS
InitB2RoundIndexArrays();
#endif
Argon2Ctx ctx = new Argon2Ctx();
ctx.Version = uVersion;
ctx.Lanes = uParallel;
ctx.TCost = uIt;
ctx.MCost = uMem / NbBlockSize;
ctx.MemoryBlocks = Math.Max(ctx.MCost, 2UL * NbSyncPoints * ctx.Lanes);
ctx.SegmentLength = ctx.MemoryBlocks / (ctx.Lanes * NbSyncPoints);
ctx.MemoryBlocks = ctx.SegmentLength * ctx.Lanes * NbSyncPoints;
ctx.LaneLength = ctx.SegmentLength * NbSyncPoints;
Debug.Assert(NbBlockSize == (NbBlockSizeInQW *
#if KeePassUAP
(ulong)Marshal.SizeOf<ulong>()
#else
(ulong)Marshal.SizeOf(typeof(ulong))
#endif
));
ctx.Mem = new ulong[ctx.MemoryBlocks * NbBlockSizeInQW];
Blake2b h = new Blake2b();
// Initial hash
Debug.Assert(h.HashSize == (NbPreHashDigestLength * 8));
byte[] pbBuf = new byte[4];
MemUtil.UInt32ToBytesEx(uParallel, pbBuf, 0);
h.TransformBlock(pbBuf, 0, pbBuf.Length, pbBuf, 0);
MemUtil.UInt32ToBytesEx((uint)cbOut, pbBuf, 0);
h.TransformBlock(pbBuf, 0, pbBuf.Length, pbBuf, 0);
MemUtil.UInt32ToBytesEx((uint)ctx.MCost, pbBuf, 0);
h.TransformBlock(pbBuf, 0, pbBuf.Length, pbBuf, 0);
MemUtil.UInt32ToBytesEx((uint)uIt, pbBuf, 0);
h.TransformBlock(pbBuf, 0, pbBuf.Length, pbBuf, 0);
MemUtil.UInt32ToBytesEx(uVersion, pbBuf, 0);
h.TransformBlock(pbBuf, 0, pbBuf.Length, pbBuf, 0);
MemUtil.UInt32ToBytesEx(0, pbBuf, 0); // Argon2d type = 0
h.TransformBlock(pbBuf, 0, pbBuf.Length, pbBuf, 0);
MemUtil.UInt32ToBytesEx((uint)pbMsg.Length, pbBuf, 0);
h.TransformBlock(pbBuf, 0, pbBuf.Length, pbBuf, 0);
h.TransformBlock(pbMsg, 0, pbMsg.Length, pbMsg, 0);
MemUtil.UInt32ToBytesEx((uint)pbSalt.Length, pbBuf, 0);
h.TransformBlock(pbBuf, 0, pbBuf.Length, pbBuf, 0);
h.TransformBlock(pbSalt, 0, pbSalt.Length, pbSalt, 0);
MemUtil.UInt32ToBytesEx((uint)pbSecretKey.Length, pbBuf, 0);
h.TransformBlock(pbBuf, 0, pbBuf.Length, pbBuf, 0);
h.TransformBlock(pbSecretKey, 0, pbSecretKey.Length, pbSecretKey, 0);
MemUtil.UInt32ToBytesEx((uint)pbAssocData.Length, pbBuf, 0);
h.TransformBlock(pbBuf, 0, pbBuf.Length, pbBuf, 0);
h.TransformBlock(pbAssocData, 0, pbAssocData.Length, pbAssocData, 0);
byte[] pbH0 = MemUtil.EmptyByteArray;
h.TransformFinalBlock(pbH0, 0, 0);
/*h.TransformFinalBlock(MemUtil.EmptyByteArray, 0, 0);
byte[] pbH0 = h.Hash;*/
Debug.Assert(pbH0.Length == 64);
byte[] pbBlockHash = new byte[NbPreHashSeedLength];
Array.Copy(pbH0, pbBlockHash, pbH0.Length);
MemUtil.ZeroByteArray(pbH0);
FillFirstBlocks(ctx, pbBlockHash, h);
MemUtil.ZeroByteArray(pbBlockHash);
FillMemoryBlocks(ctx);
byte[] pbOut = FinalHash(ctx, cbOut, h);
h.Clear();
MemUtil.ZeroArray<ulong>(ctx.Mem);
return pbOut;
}
private static void LoadBlock(ulong[] pqDst, ulong uDstOffset, byte[] pbIn)
{
// for(ulong i = 0; i < NbBlockSizeInQW; ++i)
// pqDst[uDstOffset + i] = MemUtil.BytesToUInt64(pbIn, (int)(i << 3));
Debug.Assert((uDstOffset + NbBlockSizeInQW - 1UL) <= (ulong)int.MaxValue);
int iDstOffset = (int)uDstOffset;
for(int i = 0; i < (int)NbBlockSizeInQW; ++i)
pqDst[iDstOffset + i] = MemUtil.BytesToUInt64(pbIn, i << 3);
}
private static void StoreBlock(byte[] pbDst, ulong[] pqSrc)
{
for(int i = 0; i < (int)NbBlockSizeInQW; ++i)
MemUtil.UInt64ToBytesEx(pqSrc[i], pbDst, i << 3);
}
private static void CopyBlock(ulong[] vDst, ulong uDstOffset, ulong[] vSrc,
ulong uSrcOffset)
{
// for(ulong i = 0; i < NbBlockSizeInQW; ++i)
// vDst[uDstOffset + i] = vSrc[uSrcOffset + i];
// Debug.Assert((uDstOffset + NbBlockSizeInQW - 1UL) <= (ulong)int.MaxValue);
// Debug.Assert((uSrcOffset + NbBlockSizeInQW - 1UL) <= (ulong)int.MaxValue);
// int iDstOffset = (int)uDstOffset;
// int iSrcOffset = (int)uSrcOffset;
// for(int i = 0; i < (int)NbBlockSizeInQW; ++i)
// vDst[iDstOffset + i] = vSrc[iSrcOffset + i];
Array.Copy(vSrc, (int)uSrcOffset, vDst, (int)uDstOffset,
(int)NbBlockSizeInQW);
}
private static void XorBlock(ulong[] vDst, ulong uDstOffset, ulong[] vSrc,
ulong uSrcOffset)
{
// for(ulong i = 0; i < NbBlockSizeInQW; ++i)
// vDst[uDstOffset + i] ^= vSrc[uSrcOffset + i];
Debug.Assert((uDstOffset + NbBlockSizeInQW - 1UL) <= (ulong)int.MaxValue);
Debug.Assert((uSrcOffset + NbBlockSizeInQW - 1UL) <= (ulong)int.MaxValue);
int iDstOffset = (int)uDstOffset;
int iSrcOffset = (int)uSrcOffset;
for(int i = 0; i < (int)NbBlockSizeInQW; ++i)
vDst[iDstOffset + i] ^= vSrc[iSrcOffset + i];
}
private static void Blake2bLong(byte[] pbOut, int cbOut,
byte[] pbIn, int cbIn, Blake2b h)
{
Debug.Assert((h != null) && (h.HashSize == (64 * 8)));
byte[] pbOutLen = new byte[4];
MemUtil.UInt32ToBytesEx((uint)cbOut, pbOutLen, 0);
if(cbOut <= 64)
{
Blake2b hOut = ((cbOut == 64) ? h : new Blake2b(cbOut));
if(cbOut == 64) hOut.Initialize();
hOut.TransformBlock(pbOutLen, 0, pbOutLen.Length, pbOutLen, 0);
hOut.TransformBlock(pbIn, 0, cbIn, pbIn, 0);
hOut.TransformFinalBlock(pbOut, 0, 0);
/*hOut.TransformFinalBlock(MemUtil.EmptyByteArray, 0, 0);
Array.Copy(hOut.Hash, pbOut, cbOut);*/
if(cbOut < 64) hOut.Clear();
return;
}
byte[] pbOutBuffer = new byte[64];
h.Initialize();
h.TransformBlock(pbOutLen, 0, pbOutLen.Length, pbOutLen, 0);
h.TransformBlock(pbIn, 0, cbIn, pbIn, 0);
h.TransformFinalBlock(pbOutBuffer, 0, 0);
/*h.TransformFinalBlock(MemUtil.EmptyByteArray, 0, 0);
Array.Copy(h.Hash, pbOutBuffer, pbOutBuffer.Length);*/
int ibOut = 64 / 2;
Array.Copy(pbOutBuffer, pbOut, ibOut);
int cbToProduce = cbOut - ibOut;
h.Initialize();
while(cbToProduce > 64)
{
byte[] pbHash = h.ComputeHash(pbOutBuffer);
Array.Copy(pbHash, pbOutBuffer, 64);
Array.Copy(pbHash, 0, pbOut, ibOut, 64 / 2);
ibOut += 64 / 2;
cbToProduce -= 64 / 2;
MemUtil.ZeroByteArray(pbHash);
}
/*using(*/
{
Blake2b hOut = new Blake2b(cbToProduce); /*)
{*/
byte[] pbHash = hOut.ComputeHash(pbOutBuffer);
Array.Copy(pbHash, 0, pbOut, ibOut, cbToProduce);
MemUtil.ZeroByteArray(pbHash);
//}
}
MemUtil.ZeroByteArray(pbOutBuffer);
}
#if !ARGON2_G_INLINED
private static ulong BlaMka(ulong x, ulong y)
{
ulong xy = (x & 0xFFFFFFFFUL) * (y & 0xFFFFFFFFUL);
return (x + y + (xy << 1));
}
private static void G(ulong[] v, int a, int b, int c, int d)
{
ulong va = v[a], vb = v[b], vc = v[c], vd = v[d];
va = BlaMka(va, vb);
vd = MemUtil.RotateRight64(vd ^ va, 32);
vc = BlaMka(vc, vd);
vb = MemUtil.RotateRight64(vb ^ vc, 24);
va = BlaMka(va, vb);
vd = MemUtil.RotateRight64(vd ^ va, 16);
vc = BlaMka(vc, vd);
vb = MemUtil.RotateRight64(vb ^ vc, 63);
v[a] = va;
v[b] = vb;
v[c] = vc;
v[d] = vd;
}
#else
private static void G(ulong[] v, int a, int b, int c, int d)
{
ulong va = v[a], vb = v[b], vc = v[c], vd = v[d];
ulong xy = (va & 0xFFFFFFFFUL) * (vb & 0xFFFFFFFFUL);
va += vb + (xy << 1);
vd = MemUtil.RotateRight64(vd ^ va, 32);
xy = (vc & 0xFFFFFFFFUL) * (vd & 0xFFFFFFFFUL);
vc += vd + (xy << 1);
vb = MemUtil.RotateRight64(vb ^ vc, 24);
xy = (va & 0xFFFFFFFFUL) * (vb & 0xFFFFFFFFUL);
va += vb + (xy << 1);
vd = MemUtil.RotateRight64(vd ^ va, 16);
xy = (vc & 0xFFFFFFFFUL) * (vd & 0xFFFFFFFFUL);
vc += vd + (xy << 1);
vb = MemUtil.RotateRight64(vb ^ vc, 63);
v[a] = va;
v[b] = vb;
v[c] = vc;
v[d] = vd;
}
#endif
#if ARGON2_B2ROUND_ARRAYS
private static void Blake2RoundNoMsg(ulong[] pbR, int[] v)
{
G(pbR, v[0], v[4], v[8], v[12]);
G(pbR, v[1], v[5], v[9], v[13]);
G(pbR, v[2], v[6], v[10], v[14]);
G(pbR, v[3], v[7], v[11], v[15]);
G(pbR, v[0], v[5], v[10], v[15]);
G(pbR, v[1], v[6], v[11], v[12]);
G(pbR, v[2], v[7], v[8], v[13]);
G(pbR, v[3], v[4], v[9], v[14]);
}
#else
private static void Blake2RoundNoMsgCols16i(ulong[] pbR, int i)
{
G(pbR, i, i + 4, i + 8, i + 12);
G(pbR, i + 1, i + 5, i + 9, i + 13);
G(pbR, i + 2, i + 6, i + 10, i + 14);
G(pbR, i + 3, i + 7, i + 11, i + 15);
G(pbR, i, i + 5, i + 10, i + 15);
G(pbR, i + 1, i + 6, i + 11, i + 12);
G(pbR, i + 2, i + 7, i + 8, i + 13);
G(pbR, i + 3, i + 4, i + 9, i + 14);
}
private static void Blake2RoundNoMsgRows2i(ulong[] pbR, int i)
{
G(pbR, i, i + 32, i + 64, i + 96);
G(pbR, i + 1, i + 33, i + 65, i + 97);
G(pbR, i + 16, i + 48, i + 80, i + 112);
G(pbR, i + 17, i + 49, i + 81, i + 113);
G(pbR, i, i + 33, i + 80, i + 113);
G(pbR, i + 1, i + 48, i + 81, i + 96);
G(pbR, i + 16, i + 49, i + 64, i + 97);
G(pbR, i + 17, i + 32, i + 65, i + 112);
}
#endif
private static void FillFirstBlocks(Argon2Ctx ctx, byte[] pbBlockHash,
Blake2b h)
{
byte[] pbBlock = new byte[NbBlockSize];
for(ulong l = 0; l < ctx.Lanes; ++l)
{
MemUtil.UInt32ToBytesEx(0, pbBlockHash, NbPreHashDigestLength);
MemUtil.UInt32ToBytesEx((uint)l, pbBlockHash, NbPreHashDigestLength + 4);
Blake2bLong(pbBlock, (int)NbBlockSize, pbBlockHash,
NbPreHashSeedLength, h);
LoadBlock(ctx.Mem, l * ctx.LaneLength * NbBlockSizeInQW, pbBlock);
MemUtil.UInt32ToBytesEx(1, pbBlockHash, NbPreHashDigestLength);
Blake2bLong(pbBlock, (int)NbBlockSize, pbBlockHash,
NbPreHashSeedLength, h);
LoadBlock(ctx.Mem, (l * ctx.LaneLength + 1UL) * NbBlockSizeInQW, pbBlock);
}
MemUtil.ZeroByteArray(pbBlock);
}
private static ulong IndexAlpha(Argon2Ctx ctx, Argon2ThreadInfo ti,
uint uPseudoRand, bool bSameLane)
{
ulong uRefAreaSize;
if(ti.Pass == 0)
{
if(ti.Slice == 0)
{
Debug.Assert(ti.Index > 0);
uRefAreaSize = ti.Index - 1UL;
}
else
{
if(bSameLane)
uRefAreaSize = ti.Slice * ctx.SegmentLength +
ti.Index - 1UL;
else
uRefAreaSize = ti.Slice * ctx.SegmentLength -
((ti.Index == 0UL) ? 1UL : 0UL);
}
}
else
{
if(bSameLane)
uRefAreaSize = ctx.LaneLength - ctx.SegmentLength +
ti.Index - 1UL;
else
uRefAreaSize = ctx.LaneLength - ctx.SegmentLength -
((ti.Index == 0) ? 1UL : 0UL);
}
Debug.Assert(uRefAreaSize <= (ulong)uint.MaxValue);
ulong uRelPos = uPseudoRand;
uRelPos = (uRelPos * uRelPos) >> 32;
uRelPos = uRefAreaSize - 1UL - ((uRefAreaSize * uRelPos) >> 32);
ulong uStart = 0;
if(ti.Pass != 0)
uStart = (((ti.Slice + 1UL) == NbSyncPoints) ? 0UL :
((ti.Slice + 1UL) * ctx.SegmentLength));
Debug.Assert(uStart <= (ulong)uint.MaxValue);
Debug.Assert(ctx.LaneLength <= (ulong)uint.MaxValue);
return ((uStart + uRelPos) % ctx.LaneLength);
}
private static void FillMemoryBlocks(Argon2Ctx ctx)
{
int np = (int)ctx.Lanes;
Argon2ThreadInfo[] v = new Argon2ThreadInfo[np];
for(ulong r = 0; r < ctx.TCost; ++r)
{
for(ulong s = 0; s < NbSyncPoints; ++s)
{
for(int l = 0; l < np; ++l)
{
Argon2ThreadInfo ti = new Argon2ThreadInfo();
ti.Context = ctx;
ti.Pass = r;
ti.Lane = (ulong)l;
ti.Slice = s;
/*if(!ThreadPool.QueueUserWorkItem(FillSegmentThr, ti))
{
Debug.Assert(false);
throw new OutOfMemoryException();
}*/
v[l] = ti;
}
for(int l = 0; l < np; ++l)
{
v[l].Finished.WaitOne();
v[l].Release();
}
}
}
}
private static void FillSegmentThr(object o)
{
Argon2ThreadInfo ti = (o as Argon2ThreadInfo);
if(ti == null) { Debug.Assert(false); return; }
try
{
Argon2Ctx ctx = ti.Context;
if(ctx == null) { Debug.Assert(false); return; }
Debug.Assert(ctx.Version >= MinVersion);
bool bCanXor = (ctx.Version >= 0x13U);
ulong uStart = 0;
if((ti.Pass == 0) && (ti.Slice == 0)) uStart = 2;
ulong uCur = (ti.Lane * ctx.LaneLength) + (ti.Slice *
ctx.SegmentLength) + uStart;
ulong uPrev = (((uCur % ctx.LaneLength) == 0) ?
(uCur + ctx.LaneLength - 1UL) : (uCur - 1UL));
ulong[] pbR = new ulong[NbBlockSizeInQW];
ulong[] pbTmp = new ulong[NbBlockSizeInQW];
for(ulong i = uStart; i < ctx.SegmentLength; ++i)
{
if((uCur % ctx.LaneLength) == 1)
uPrev = uCur - 1UL;
ulong uPseudoRand = ctx.Mem[uPrev * NbBlockSizeInQW];
ulong uRefLane = (uPseudoRand >> 32) % ctx.Lanes;
if((ti.Pass == 0) && (ti.Slice == 0))
uRefLane = ti.Lane;
ti.Index = i;
ulong uRefIndex = IndexAlpha(ctx, ti, (uint)uPseudoRand,
(uRefLane == ti.Lane));
ulong uRefBlockIndex = (ctx.LaneLength * uRefLane +
uRefIndex) * NbBlockSizeInQW;
ulong uCurBlockIndex = uCur * NbBlockSizeInQW;
FillBlock(ctx.Mem, uPrev * NbBlockSizeInQW, uRefBlockIndex,
uCurBlockIndex, ((ti.Pass != 0) && bCanXor), pbR, pbTmp);
++uCur;
++uPrev;
}
MemUtil.ZeroArray<ulong>(pbR);
MemUtil.ZeroArray<ulong>(pbTmp);
}
catch(Exception) { Debug.Assert(false); }
try { ti.Finished.Set(); }
catch(Exception) { Debug.Assert(false); }
}
#if ARGON2_B2ROUND_ARRAYS
private static void InitB2RoundIndexArrays()
{
int[][] vCols = g_vFBCols;
if(vCols == null)
{
vCols = new int[8][];
Debug.Assert(vCols.Length == 8);
int e = 0;
for(int i = 0; i < 8; ++i)
{
vCols[i] = new int[16];
for(int j = 0; j < 16; ++j)
{
vCols[i][j] = e;
++e;
}
}
g_vFBCols = vCols;
}
int[][] vRows = g_vFBRows;
if(vRows == null)
{
vRows = new int[8][];
for(int i = 0; i < 8; ++i)
{
vRows[i] = new int[16];
for(int j = 0; j < 16; ++j)
{
int jh = j / 2;
vRows[i][j] = (2 * i) + (16 * jh) + (j & 1);
}
}
g_vFBRows = vRows;
}
}
#endif
private static void FillBlock(ulong[] pMem, ulong uPrev, ulong uRef,
ulong uNext, bool bXor, ulong[] pbR, ulong[] pbTmp)
{
CopyBlock(pbR, 0, pMem, uRef);
XorBlock(pbR, 0, pMem, uPrev);
CopyBlock(pbTmp, 0, pbR, 0);
if(bXor) XorBlock(pbTmp, 0, pMem, uNext);
#if ARGON2_B2ROUND_ARRAYS
int[][] vCols = g_vFBCols;
int[][] vRows = g_vFBRows;
for(int i = 0; i < 8; ++i)
Blake2RoundNoMsg(pbR, vCols[i]);
for(int i = 0; i < 8; ++i)
Blake2RoundNoMsg(pbR, vRows[i]);
#else
for(int i = 0; i < (8 * 16); i += 16)
Blake2RoundNoMsgCols16i(pbR, i);
for(int i = 0; i < (8 * 2); i += 2)
Blake2RoundNoMsgRows2i(pbR, i);
#endif
CopyBlock(pMem, uNext, pbTmp, 0);
XorBlock(pMem, uNext, pbR, 0);
}
private static byte[] FinalHash(Argon2Ctx ctx, int cbOut, Blake2b h)
{
ulong[] pqBlockHash = new ulong[NbBlockSizeInQW];
CopyBlock(pqBlockHash, 0, ctx.Mem, (ctx.LaneLength - 1UL) *
NbBlockSizeInQW);
for(ulong l = 1; l < ctx.Lanes; ++l)
XorBlock(pqBlockHash, 0, ctx.Mem, (l * ctx.LaneLength +
ctx.LaneLength - 1UL) * NbBlockSizeInQW);
byte[] pbBlockHashBytes = new byte[NbBlockSize];
StoreBlock(pbBlockHashBytes, pqBlockHash);
byte[] pbOut = new byte[cbOut];
Blake2bLong(pbOut, cbOut, pbBlockHashBytes, (int)NbBlockSize, h);
MemUtil.ZeroArray<ulong>(pqBlockHash);
MemUtil.ZeroByteArray(pbBlockHashBytes);
return pbOut;
}
}
}

View File

@@ -0,0 +1,144 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2017 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.Collections.Generic;
using System.Diagnostics;
using System.Text;
namespace ModernKeePassLib.Cryptography.KeyDerivation
{
public sealed partial class Argon2Kdf : KdfEngine
{
private static readonly PwUuid g_uuid = new PwUuid(new byte[] {
0xEF, 0x63, 0x6D, 0xDF, 0x8C, 0x29, 0x44, 0x4B,
0x91, 0xF7, 0xA9, 0xA4, 0x03, 0xE3, 0x0A, 0x0C });
public const string ParamSalt = "S"; // Byte[]
public const string ParamParallelism = "P"; // UInt32
public const string ParamMemory = "M"; // UInt64
public const string ParamIterations = "I"; // UInt64
public const string ParamVersion = "V"; // UInt32
public const string ParamSecretKey = "K"; // Byte[]
public const string ParamAssocData = "A"; // Byte[]
private const uint MinVersion = 0x10;
private const uint MaxVersion = 0x13;
private const int MinSalt = 8;
private const int MaxSalt = int.MaxValue; // .NET limit; 2^32 - 1 in spec
internal const ulong MinIterations = 1;
internal const ulong MaxIterations = uint.MaxValue;
internal const ulong MinMemory = 1024 * 8; // For parallelism = 1
// internal const ulong MaxMemory = (ulong)uint.MaxValue * 1024UL; // Spec
internal const ulong MaxMemory = int.MaxValue; // .NET limit
internal const uint MinParallelism = 1;
internal const uint MaxParallelism = (1 << 24) - 1;
internal const ulong DefaultIterations = 2;
internal const ulong DefaultMemory = 1024 * 1024; // 1 MB
internal const uint DefaultParallelism = 2;
public override PwUuid Uuid
{
get { return g_uuid; }
}
public override string Name
{
get { return "Argon2"; }
}
public Argon2Kdf()
{
}
public override KdfParameters GetDefaultParameters()
{
KdfParameters p = base.GetDefaultParameters();
p.SetUInt32(ParamVersion, MaxVersion);
p.SetUInt64(ParamIterations, DefaultIterations);
p.SetUInt64(ParamMemory, DefaultMemory);
p.SetUInt32(ParamParallelism, DefaultParallelism);
return p;
}
public override void Randomize(KdfParameters p)
{
if(p == null) { Debug.Assert(false); return; }
Debug.Assert(g_uuid.Equals(p.KdfUuid));
byte[] pb = CryptoRandom.Instance.GetRandomBytes(32);
p.SetByteArray(ParamSalt, pb);
}
public override byte[] Transform(byte[] pbMsg, KdfParameters p)
{
if(pbMsg == null) throw new ArgumentNullException("pbMsg");
if(p == null) throw new ArgumentNullException("p");
byte[] pbSalt = p.GetByteArray(ParamSalt);
if(pbSalt == null)
throw new ArgumentNullException("p.Salt");
if((pbSalt.Length < MinSalt) || (pbSalt.Length > MaxSalt))
throw new ArgumentOutOfRangeException("p.Salt");
uint uPar = p.GetUInt32(ParamParallelism, 0);
if((uPar < MinParallelism) || (uPar > MaxParallelism))
throw new ArgumentOutOfRangeException("p.Parallelism");
ulong uMem = p.GetUInt64(ParamMemory, 0);
if((uMem < MinMemory) || (uMem > MaxMemory))
throw new ArgumentOutOfRangeException("p.Memory");
ulong uIt = p.GetUInt64(ParamIterations, 0);
if((uIt < MinIterations) || (uIt > MaxIterations))
throw new ArgumentOutOfRangeException("p.Iterations");
uint v = p.GetUInt32(ParamVersion, 0);
if((v < MinVersion) || (v > MaxVersion))
throw new ArgumentOutOfRangeException("p.Version");
byte[] pbSecretKey = p.GetByteArray(ParamSecretKey);
byte[] pbAssocData = p.GetByteArray(ParamAssocData);
byte[] pbRet = Argon2d(pbMsg, pbSalt, uPar, uMem, uIt,
32, v, pbSecretKey, pbAssocData);
if(uMem > (100UL * 1024UL * 1024UL)) GC.Collect();
return pbRet;
}
public override KdfParameters GetBestParameters(uint uMilliseconds)
{
KdfParameters p = GetDefaultParameters();
Randomize(p);
MaximizeParamUInt64(p, ParamIterations, MinIterations,
MaxIterations, uMilliseconds, true);
return p;
}
}
}

View File

@@ -0,0 +1,142 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2017 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.Collections.Generic;
using System.Diagnostics;
using System.Text;
namespace ModernKeePassLib.Cryptography.KeyDerivation
{
public abstract class KdfEngine
{
public abstract PwUuid Uuid
{
get;
}
public abstract string Name
{
get;
}
public virtual KdfParameters GetDefaultParameters()
{
return new KdfParameters(this.Uuid);
}
/// <summary>
/// Generate random seeds and store them in <paramref name="p" />.
/// </summary>
public virtual void Randomize(KdfParameters p)
{
Debug.Assert(p != null);
Debug.Assert(p.KdfUuid.Equals(this.Uuid));
}
public abstract byte[] Transform(byte[] pbMsg, KdfParameters p);
public virtual KdfParameters GetBestParameters(uint uMilliseconds)
{
throw new NotImplementedException();
}
protected void MaximizeParamUInt64(KdfParameters p, string strName,
ulong uMin, ulong uMax, uint uMilliseconds, bool bInterpSearch)
{
if(p == null) { Debug.Assert(false); return; }
if(string.IsNullOrEmpty(strName)) { Debug.Assert(false); return; }
if(uMin > uMax) { Debug.Assert(false); return; }
if(uMax > (ulong.MaxValue >> 1))
{
Debug.Assert(false);
uMax = ulong.MaxValue >> 1;
if(uMin > uMax) { p.SetUInt64(strName, uMin); return; }
}
byte[] pbMsg = new byte[32];
for(int i = 0; i < pbMsg.Length; ++i) pbMsg[i] = (byte)i;
ulong uLow = uMin;
ulong uHigh = uMin + 1UL;
long tLow = 0;
long tHigh = 0;
long tTarget = (long)uMilliseconds;
// Determine range
while(uHigh <= uMax)
{
p.SetUInt64(strName, uHigh);
// GC.Collect();
Stopwatch sw = Stopwatch.StartNew();
Transform(pbMsg, p);
sw.Stop();
tHigh = sw.ElapsedMilliseconds;
if(tHigh > tTarget) break;
uLow = uHigh;
tLow = tHigh;
uHigh <<= 1;
}
if(uHigh > uMax) { uHigh = uMax; tHigh = 0; }
if(uLow > uHigh) uLow = uHigh; // Skips to end
// Find optimal number of iterations
while((uHigh - uLow) >= 2UL)
{
ulong u = (uHigh + uLow) >> 1; // Binary search
// Interpolation search, if possible
if(bInterpSearch && (tLow > 0) && (tHigh > tTarget) &&
(tLow <= tTarget))
{
u = uLow + (((uHigh - uLow) * (ulong)(tTarget - tLow)) /
(ulong)(tHigh - tLow));
if((u >= uLow) && (u <= uHigh))
{
u = Math.Max(u, uLow + 1UL);
u = Math.Min(u, uHigh - 1UL);
}
else
{
Debug.Assert(false);
u = (uHigh + uLow) >> 1;
}
}
p.SetUInt64(strName, u);
// GC.Collect();
Stopwatch sw = Stopwatch.StartNew();
Transform(pbMsg, p);
sw.Stop();
long t = sw.ElapsedMilliseconds;
if(t == tTarget) { uLow = u; break; }
else if(t > tTarget) { uHigh = u; tHigh = t; }
else { uLow = u; tLow = t; }
}
p.SetUInt64(strName, uLow);
}
}
}

View File

@@ -0,0 +1,80 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2017 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.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;
using ModernKeePassLib.Collections;
using ModernKeePassLib.Utility;
namespace ModernKeePassLib.Cryptography.KeyDerivation
{
public sealed class KdfParameters : VariantDictionary
{
private const string ParamUuid = @"$UUID";
private readonly PwUuid m_puKdf;
public PwUuid KdfUuid
{
get { return m_puKdf; }
}
public KdfParameters(PwUuid puKdf)
{
if(puKdf == null) throw new ArgumentNullException("puKdf");
m_puKdf = puKdf;
SetByteArray(ParamUuid, puKdf.UuidBytes);
}
/// <summary>
/// Unsupported.
/// </summary>
public override object Clone()
{
throw new NotSupportedException();
}
public static byte[] SerializeExt(KdfParameters p)
{
return VariantDictionary.Serialize(p);
}
public static KdfParameters DeserializeExt(byte[] pb)
{
VariantDictionary d = VariantDictionary.Deserialize(pb);
if(d == null) { Debug.Assert(false); return null; }
byte[] pbUuid = d.GetByteArray(ParamUuid);
if((pbUuid == null) || (pbUuid.Length != (int)PwUuid.UuidSize))
{
Debug.Assert(false);
return null;
}
PwUuid pu = new PwUuid(pbUuid);
KdfParameters p = new KdfParameters(pu);
d.CopyTo(p);
return p;
}
}
}

View File

@@ -0,0 +1,96 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2017 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.Collections.Generic;
using System.Diagnostics;
using System.Text;
using ModernKeePassLib.Utility;
namespace ModernKeePassLib.Cryptography.KeyDerivation
{
public static class KdfPool
{
private static List<KdfEngine> g_l = new List<KdfEngine>();
public static IEnumerable<KdfEngine> Engines
{
get
{
EnsureInitialized();
return g_l;
}
}
private static void EnsureInitialized()
{
if(g_l.Count > 0) return;
g_l.Add(new AesKdf());
g_l.Add(new Argon2Kdf());
}
internal static KdfParameters GetDefaultParameters()
{
EnsureInitialized();
return g_l[0].GetDefaultParameters();
}
public static KdfEngine Get(PwUuid pu)
{
if(pu == null) { Debug.Assert(false); return null; }
EnsureInitialized();
foreach(KdfEngine kdf in g_l)
{
if(pu.Equals(kdf.Uuid)) return kdf;
}
return null;
}
public static KdfEngine Get(string strName)
{
if(string.IsNullOrEmpty(strName)) { Debug.Assert(false); return null; }
EnsureInitialized();
foreach(KdfEngine kdf in g_l)
{
if(strName.Equals(kdf.Name, StrUtil.CaseIgnoreCmp)) return kdf;
}
return null;
}
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);
}
}
}

View File

@@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2014 Dominik Reichl <dominik.reichl@t-online.de> Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -47,7 +47,7 @@ namespace ModernKeePassLib.Cryptography.PasswordGenerator
if(ch == char.MinValue) if(ch == char.MinValue)
{ {
Array.Clear(vGenerated, 0, vGenerated.Length); MemUtil.ZeroArray<char>(vGenerated);
return PwgError.TooFewCharacters; return PwgError.TooFewCharacters;
} }
@@ -57,7 +57,7 @@ namespace ModernKeePassLib.Cryptography.PasswordGenerator
byte[] pbUtf8 = StrUtil.Utf8.GetBytes(vGenerated); byte[] pbUtf8 = StrUtil.Utf8.GetBytes(vGenerated);
psOut = new ProtectedString(true, pbUtf8); psOut = new ProtectedString(true, pbUtf8);
MemUtil.ZeroByteArray(pbUtf8); MemUtil.ZeroByteArray(pbUtf8);
Array.Clear(vGenerated, 0, vGenerated.Length); MemUtil.ZeroArray<char>(vGenerated);
return PwgError.Success; return PwgError.Success;
} }

View File

@@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2014 Dominik Reichl <dominik.reichl@t-online.de> Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by

View File

@@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2014 Dominik Reichl <dominik.reichl@t-online.de> Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by

View File

@@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2014 Dominik Reichl <dominik.reichl@t-online.de> Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -132,7 +132,7 @@ namespace ModernKeePassLib.Cryptography.PasswordGenerator
byte[] pbUtf8 = StrUtil.Utf8.GetBytes(vArray); byte[] pbUtf8 = StrUtil.Utf8.GetBytes(vArray);
psOut = new ProtectedString(true, pbUtf8); psOut = new ProtectedString(true, pbUtf8);
MemUtil.ZeroByteArray(pbUtf8); MemUtil.ZeroByteArray(pbUtf8);
Array.Clear(vArray, 0, vArray.Length); MemUtil.ZeroArray<char>(vArray);
vGenerated.Clear(); vGenerated.Clear();
return PwgError.Success; return PwgError.Success;

View File

@@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2014 Dominik Reichl <dominik.reichl@t-online.de> Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by

View File

@@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2014 Dominik Reichl <dominik.reichl@t-online.de> Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -19,10 +19,13 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.Text; using System.Text;
using System.Diagnostics; using System.Diagnostics;
using Windows.Security.Cryptography;
using Windows.Security.Cryptography.Core;
using ModernKeePassLib.Security; using ModernKeePassLib.Security;
using ModernKeePassLib.Utility;
namespace ModernKeePassLib.Cryptography.PasswordGenerator namespace ModernKeePassLib.Cryptography.PasswordGenerator
{ {
@@ -46,32 +49,56 @@ namespace ModernKeePassLib.Cryptography.PasswordGenerator
Debug.Assert(pwProfile != null); Debug.Assert(pwProfile != null);
if(pwProfile == null) throw new ArgumentNullException("pwProfile"); if(pwProfile == null) throw new ArgumentNullException("pwProfile");
CryptoRandomStream crs = CreateCryptoStream(pbUserEntropy);
PwgError e = PwgError.Unknown; PwgError e = PwgError.Unknown;
CryptoRandomStream crs = null;
byte[] pbKey = null;
try
{
crs = CreateRandomStream(pbUserEntropy, out pbKey);
if(pwProfile.GeneratorType == PasswordGeneratorType.CharSet) if(pwProfile.GeneratorType == PasswordGeneratorType.CharSet)
e = CharSetBasedGenerator.Generate(out psOut, pwProfile, crs); e = CharSetBasedGenerator.Generate(out psOut, pwProfile, crs);
else if(pwProfile.GeneratorType == PasswordGeneratorType.Pattern) else if(pwProfile.GeneratorType == PasswordGeneratorType.Pattern)
e = PatternBasedGenerator.Generate(out psOut, pwProfile, crs); e = PatternBasedGenerator.Generate(out psOut, pwProfile, crs);
else if(pwProfile.GeneratorType == PasswordGeneratorType.Custom) else if(pwProfile.GeneratorType == PasswordGeneratorType.Custom)
e = GenerateCustom(out psOut, pwProfile, crs, pwAlgorithmPool); e = GenerateCustom(out psOut, pwProfile, crs, pwAlgorithmPool);
else { Debug.Assert(false); psOut = ProtectedString.Empty; } else { Debug.Assert(false); psOut = ProtectedString.Empty; }
}
finally
{
if(crs != null) crs.Dispose();
if(pbKey != null) MemUtil.ZeroByteArray(pbKey);
}
return e; return e;
} }
private static CryptoRandomStream CreateCryptoStream(byte[] pbAdditionalEntropy) private static CryptoRandomStream CreateRandomStream(byte[] pbAdditionalEntropy,
out byte[] pbKey)
{ {
byte[] pbKey = CryptoRandom.Instance.GetRandomBytes(256); pbKey = CryptoRandom.Instance.GetRandomBytes(128);
// Mix in additional entropy // Mix in additional entropy
Debug.Assert(pbKey.Length >= 64);
if((pbAdditionalEntropy != null) && (pbAdditionalEntropy.Length > 0)) if((pbAdditionalEntropy != null) && (pbAdditionalEntropy.Length > 0))
{ {
for(int nKeyPos = 0; nKeyPos < pbKey.Length; ++nKeyPos) #if ModernKeePassLib
pbKey[nKeyPos] ^= pbAdditionalEntropy[nKeyPos % pbAdditionalEntropy.Length]; var sha256 = HashAlgorithmProvider.OpenAlgorithm(HashAlgorithmNames.Sha256);
var buffer = sha256.HashData(CryptographicBuffer.CreateFromByteArray(pbAdditionalEntropy));
byte[] pbHash;
CryptographicBuffer.CopyToByteArray(buffer, out pbHash);
MemUtil.XorArray(pbHash, 0, pbKey, 0, pbHash.Length);
#else
using(SHA512Managed h = new SHA512Managed())
{
byte[] pbHash = h.ComputeHash(pbAdditionalEntropy);
MemUtil.XorArray(pbHash, 0, pbKey, 0, pbHash.Length);
}
#endif
} }
return new CryptoRandomStream(CrsAlgorithm.Salsa20, pbKey); return new CryptoRandomStream(CrsAlgorithm.ChaCha20, pbKey);
} }
internal static char GenerateCharacter(PwProfile pwProfile, internal static char GenerateCharacter(PwProfile pwProfile,

View File

@@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2014 Dominik Reichl <dominik.reichl@t-online.de> Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by

View File

@@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2014 Dominik Reichl <dominik.reichl@t-online.de> Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by

View File

@@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2014 Dominik Reichl <dominik.reichl@t-online.de> Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -281,7 +281,7 @@ namespace ModernKeePassLib.Cryptography
} }
} }
private static object m_objSyncInit = new object(); private static readonly object m_objSyncInit = new object();
private static List<QeCharType> m_lCharTypes = null; private static List<QeCharType> m_lCharTypes = null;
private static void EnsureInitialized() private static void EnsureInitialized()
@@ -422,7 +422,7 @@ namespace ModernKeePassLib.Cryptography
char[] vChars = StrUtil.Utf8.GetChars(pbUnprotectedUtf8); char[] vChars = StrUtil.Utf8.GetChars(pbUnprotectedUtf8);
uint uResult = EstimatePasswordBits(vChars); uint uResult = EstimatePasswordBits(vChars);
Array.Clear(vChars, 0, vChars.Length); MemUtil.ZeroArray<char>(vChars);
return uResult; return uResult;
} }

View File

@@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2014 Dominik Reichl <dominik.reichl@t-online.de> Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -28,30 +28,29 @@ using System.Security.Cryptography;
using System.Text; using System.Text;
using System.Globalization; using System.Globalization;
using System.Diagnostics; using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Security.Cryptography.Core;
using ModernKeePassLib.Cryptography.Cipher; using ModernKeePassLib.Cryptography.Cipher;
using ModernKeePassLib.Cryptography.Hash;
using ModernKeePassLib.Cryptography.KeyDerivation;
using ModernKeePassLib.Keys; using ModernKeePassLib.Keys;
using ModernKeePassLib.Native; using ModernKeePassLib.Native;
using ModernKeePassLib.Utility; using ModernKeePassLib.Utility;
using ModernKeePassLib.Resources; using ModernKeePassLib.Resources;
using ModernKeePassLib.Security; using ModernKeePassLib.Security;
using Org.BouncyCastle.Crypto.Digests;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Macs;
using Org.BouncyCastle.Crypto.Parameters;
using KdfParameters = Org.BouncyCastle.Crypto.Parameters.KdfParameters;
namespace ModernKeePassLib.Cryptography namespace ModernKeePassLib.Cryptography
{ {
/* /// <summary>
/// Return values of the <c>SelfTest.Perform</c> method.
/// </summary>
public enum SelfTestResult
{
Success = 0,
RijndaelEcbError = 1,
Salsa20Error = 2,
NativeKeyTransformationError = 3
} */
/// <summary> /// <summary>
/// Class containing self-test methods. /// Class containing self-test methods.
/// </summary> /// </summary>
// TODO: move all this into the Unit Tests project
public static class SelfTest public static class SelfTest
{ {
/// <summary> /// <summary>
@@ -59,47 +58,63 @@ namespace ModernKeePassLib.Cryptography
/// </summary> /// </summary>
public static void Perform() public static void Perform()
{ {
Random r = CryptoRandom.NewWeakRandom();
TestFipsComplianceProblems(); // Must be the first test TestFipsComplianceProblems(); // Must be the first test
TestRijndael(); TestAes();
TestSalsa20(); TestSalsa20(r);
TestChaCha20(r);
TestBlake2b(r);
TestArgon2();
TestHmac();
TestKeyTransform(r);
#if !ModernKeePassLib #if !ModernKeePassLib
TestNativeKeyTransform(); TestNativeKeyTransform(r);
#endif #endif
TestHmacOtp(); TestHmacOtp();
TestProtectedObjects(); TestProtectedObjects(r);
TestMemUtil(); TestMemUtil(r);
TestStrUtil(); TestStrUtil();
TestUrlUtil(); TestUrlUtil();
Debug.Assert((int)PwIcon.World == 1); Debug.Assert((int)PwIcon.World == 1);
Debug.Assert((int)PwIcon.Warning == 2); Debug.Assert((int)PwIcon.Warning == 2);
Debug.Assert((int)PwIcon.BlackBerry == 68); Debug.Assert((int)PwIcon.BlackBerry == 68);
#if KeePassUAP
SelfTestEx.Perform();
#endif
} }
internal static void TestFipsComplianceProblems() internal static void TestFipsComplianceProblems()
{ {
#if !ModernKeePassLib && !KeePassRT #if !ModernKeePassLib
try { new RijndaelManaged(); } try { using(RijndaelManaged r = new RijndaelManaged()) { } }
catch(Exception exAes) catch(Exception exAes)
{ {
throw new SecurityException("AES/Rijndael: " + exAes.Message); throw new SecurityException("AES/Rijndael: " + exAes.Message);
} }
#endif
try { new SHA256Managed(); } #if ModernKeePassLib
try
{
HashAlgorithmProvider.OpenAlgorithm(HashAlgorithmNames.Sha256);
}
#else
try { using(SHA256Managed h = new SHA256Managed()) { } }
#endif
catch(Exception exSha256) catch(Exception exSha256)
{ {
throw new SecurityException("SHA-256: " + exSha256.Message); throw new SecurityException("SHA-256: " + exSha256.Message);
} }
#endif
} }
private static void TestRijndael() private static void TestAes()
{ {
#if !ModernKeePassLib && !KeePassRT
// Test vector (official ECB test vector #356) // Test vector (official ECB test vector #356)
byte[] pbIV = new byte[16]; byte[] pbIV = new byte[16];
byte[] pbTestKey = new byte[32]; byte[] pbTestKey = new byte[32];
@@ -114,31 +129,38 @@ namespace ModernKeePassLib.Cryptography
for(i = 0; i < 16; ++i) pbTestData[i] = 0; for(i = 0; i < 16; ++i) pbTestData[i] = 0;
pbTestData[0] = 0x04; pbTestData[0] = 0x04;
RijndaelManaged r = new RijndaelManaged(); #if ModernKeePassLib
AesEngine r = new AesEngine();
if(r.BlockSize != 128) // AES block size r.Init(true, new KeyParameter(pbTestKey));
if(r.GetBlockSize() != pbTestData.Length)
throw new SecurityException("AES (BC)");
r.ProcessBlock(pbTestData, 0, pbTestData, 0);
#else
SymmetricAlgorithm a = CryptoUtil.CreateAes();
if(a.BlockSize != 128) // AES block size
{ {
Debug.Assert(false); Debug.Assert(false);
r.BlockSize = 128; a.BlockSize = 128;
} }
r.IV = pbIV; a.IV = pbIV;
r.KeySize = 256; a.KeySize = 256;
r.Key = pbTestKey; a.Key = pbTestKey;
r.Mode = CipherMode.ECB; a.Mode = CipherMode.ECB;
ICryptoTransform iCrypt = r.CreateEncryptor(); ICryptoTransform iCrypt = a.CreateEncryptor();
iCrypt.TransformBlock(pbTestData, 0, 16, pbTestData, 0); iCrypt.TransformBlock(pbTestData, 0, 16, pbTestData, 0);
#endif
if(!MemUtil.ArraysEqual(pbTestData, pbReferenceCT)) if(!MemUtil.ArraysEqual(pbTestData, pbReferenceCT))
throw new SecurityException(KLRes.EncAlgorithmAes + "."); throw new SecurityException("AES");
#endif
} }
private static void TestSalsa20() private static void TestSalsa20(Random r)
{ {
#if DEBUG
// Test values from official set 6, vector 3 // Test values from official set 6, vector 3
byte[] pbKey= new byte[32] { byte[] pbKey = new byte[32] {
0x0F, 0x62, 0xB5, 0x08, 0x5B, 0xAE, 0x01, 0x54, 0x0F, 0x62, 0xB5, 0x08, 0x5B, 0xAE, 0x01, 0x54,
0xA7, 0xFA, 0x4D, 0xA0, 0xF3, 0x46, 0x99, 0xEC, 0xA7, 0xFA, 0x4D, 0xA0, 0xF3, 0x46, 0x99, 0xEC,
0x3F, 0x92, 0xE5, 0x38, 0x8B, 0xDE, 0x31, 0x84, 0x3F, 0x92, 0xE5, 0x38, 0x8B, 0xDE, 0x31, 0x84,
@@ -153,12 +175,11 @@ namespace ModernKeePassLib.Cryptography
byte[] pb = new byte[16]; byte[] pb = new byte[16];
Salsa20Cipher c = new Salsa20Cipher(pbKey, pbIV); Salsa20Cipher c = new Salsa20Cipher(pbKey, pbIV);
c.Encrypt(pb, pb.Length, false); c.Encrypt(pb, 0, pb.Length);
if(!MemUtil.ArraysEqual(pb, pbExpected)) if(!MemUtil.ArraysEqual(pb, pbExpected))
throw new SecurityException("Salsa20-1"); throw new SecurityException("Salsa20-1");
#if DEBUG // Extended test
// Extended test in debug mode
byte[] pbExpected2 = new byte[16] { byte[] pbExpected2 = new byte[16] {
0xAB, 0xF3, 0x9A, 0x21, 0x0E, 0xEE, 0x89, 0x59, 0xAB, 0xF3, 0x9A, 0x21, 0x0E, 0xEE, 0x89, 0x59,
0x8B, 0x71, 0x33, 0x37, 0x70, 0x56, 0xC2, 0xFE 0x8B, 0x71, 0x33, 0x37, 0x70, 0x56, 0xC2, 0xFE
@@ -168,15 +189,15 @@ namespace ModernKeePassLib.Cryptography
0x28, 0xF5, 0x67, 0x91, 0xD5, 0xB7, 0xCE, 0x23 0x28, 0xF5, 0x67, 0x91, 0xD5, 0xB7, 0xCE, 0x23
}; };
Random r = new Random();
int nPos = Salsa20ToPos(c, r, pb.Length, 65536); int nPos = Salsa20ToPos(c, r, pb.Length, 65536);
c.Encrypt(pb, pb.Length, false); Array.Clear(pb, 0, pb.Length);
c.Encrypt(pb, 0, pb.Length);
if(!MemUtil.ArraysEqual(pb, pbExpected2)) if(!MemUtil.ArraysEqual(pb, pbExpected2))
throw new SecurityException("Salsa20-2"); throw new SecurityException("Salsa20-2");
nPos = Salsa20ToPos(c, r, nPos + pb.Length, 131008); nPos = Salsa20ToPos(c, r, nPos + pb.Length, 131008);
Array.Clear(pb, 0, pb.Length); Array.Clear(pb, 0, pb.Length);
c.Encrypt(pb, pb.Length, true); c.Encrypt(pb, 0, pb.Length);
if(!MemUtil.ArraysEqual(pb, pbExpected3)) if(!MemUtil.ArraysEqual(pb, pbExpected3))
throw new SecurityException("Salsa20-3"); throw new SecurityException("Salsa20-3");
@@ -185,8 +206,8 @@ namespace ModernKeePassLib.Cryptography
for(int i = 0; i < nRounds; ++i) for(int i = 0; i < nRounds; ++i)
{ {
byte[] z = new byte[32]; byte[] z = new byte[32];
c = new Salsa20Cipher(z, BitConverter.GetBytes((long)i)); c = new Salsa20Cipher(z, MemUtil.Int64ToBytes(i));
c.Encrypt(z, z.Length, true); c.Encrypt(z, 0, z.Length);
d[MemUtil.ByteArrayToHexString(z)] = true; d[MemUtil.ByteArrayToHexString(z)] = true;
} }
if(d.Count != nRounds) throw new SecurityException("Salsa20-4"); if(d.Count != nRounds) throw new SecurityException("Salsa20-4");
@@ -203,7 +224,7 @@ namespace ModernKeePassLib.Cryptography
{ {
int x = r.Next(1, 513); int x = r.Next(1, 513);
int nGen = Math.Min(nTargetPos - nPos, x); int nGen = Math.Min(nTargetPos - nPos, x);
c.Encrypt(pb, nGen, r.Next(0, 2) == 0); c.Encrypt(pb, 0, nGen);
nPos += nGen; nPos += nGen;
} }
@@ -211,34 +232,571 @@ namespace ModernKeePassLib.Cryptography
} }
#endif #endif
private static void TestChaCha20(Random r)
{
// ======================================================
// Test vector from RFC 7539, section 2.3.2
byte[] pbKey = new byte[32];
for(int i = 0; i < 32; ++i) pbKey[i] = (byte)i;
byte[] pbIV = new byte[12];
pbIV[3] = 0x09;
pbIV[7] = 0x4A;
byte[] pbExpc = new byte[64] {
0x10, 0xF1, 0xE7, 0xE4, 0xD1, 0x3B, 0x59, 0x15,
0x50, 0x0F, 0xDD, 0x1F, 0xA3, 0x20, 0x71, 0xC4,
0xC7, 0xD1, 0xF4, 0xC7, 0x33, 0xC0, 0x68, 0x03,
0x04, 0x22, 0xAA, 0x9A, 0xC3, 0xD4, 0x6C, 0x4E,
0xD2, 0x82, 0x64, 0x46, 0x07, 0x9F, 0xAA, 0x09,
0x14, 0xC2, 0xD7, 0x05, 0xD9, 0x8B, 0x02, 0xA2,
0xB5, 0x12, 0x9C, 0xD1, 0xDE, 0x16, 0x4E, 0xB9,
0xCB, 0xD0, 0x83, 0xE8, 0xA2, 0x50, 0x3C, 0x4E
};
byte[] pb = new byte[64];
using(ChaCha20Cipher c = new ChaCha20Cipher(pbKey, pbIV))
{
c.Seek(64, SeekOrigin.Begin); // Skip first block
c.Encrypt(pb, 0, pb.Length);
if(!MemUtil.ArraysEqual(pb, pbExpc))
throw new SecurityException("ChaCha20-1");
}
#if DEBUG
// ======================================================
// Test vector from RFC 7539, section 2.4.2
pbIV[3] = 0;
pb = StrUtil.Utf8.GetBytes("Ladies and Gentlemen of the clas" +
@"s of '99: If I could offer you only one tip for " +
@"the future, sunscreen would be it.");
pbExpc = new byte[] {
0x6E, 0x2E, 0x35, 0x9A, 0x25, 0x68, 0xF9, 0x80,
0x41, 0xBA, 0x07, 0x28, 0xDD, 0x0D, 0x69, 0x81,
0xE9, 0x7E, 0x7A, 0xEC, 0x1D, 0x43, 0x60, 0xC2,
0x0A, 0x27, 0xAF, 0xCC, 0xFD, 0x9F, 0xAE, 0x0B,
0xF9, 0x1B, 0x65, 0xC5, 0x52, 0x47, 0x33, 0xAB,
0x8F, 0x59, 0x3D, 0xAB, 0xCD, 0x62, 0xB3, 0x57,
0x16, 0x39, 0xD6, 0x24, 0xE6, 0x51, 0x52, 0xAB,
0x8F, 0x53, 0x0C, 0x35, 0x9F, 0x08, 0x61, 0xD8,
0x07, 0xCA, 0x0D, 0xBF, 0x50, 0x0D, 0x6A, 0x61,
0x56, 0xA3, 0x8E, 0x08, 0x8A, 0x22, 0xB6, 0x5E,
0x52, 0xBC, 0x51, 0x4D, 0x16, 0xCC, 0xF8, 0x06,
0x81, 0x8C, 0xE9, 0x1A, 0xB7, 0x79, 0x37, 0x36,
0x5A, 0xF9, 0x0B, 0xBF, 0x74, 0xA3, 0x5B, 0xE6,
0xB4, 0x0B, 0x8E, 0xED, 0xF2, 0x78, 0x5E, 0x42,
0x87, 0x4D
};
byte[] pb64 = new byte[64];
using(ChaCha20Cipher c = new ChaCha20Cipher(pbKey, pbIV))
{
c.Encrypt(pb64, 0, pb64.Length); // Skip first block
c.Encrypt(pb, 0, pb.Length);
if(!MemUtil.ArraysEqual(pb, pbExpc))
throw new SecurityException("ChaCha20-2");
}
// ======================================================
// Test vector from RFC 7539, appendix A.2 #2
Array.Clear(pbKey, 0, pbKey.Length);
pbKey[31] = 1;
Array.Clear(pbIV, 0, pbIV.Length);
pbIV[11] = 2;
pb = StrUtil.Utf8.GetBytes("Any submission to the IETF inten" +
"ded by the Contributor for publication as all or" +
" part of an IETF Internet-Draft or RFC and any s" +
"tatement made within the context of an IETF acti" +
"vity is considered an \"IETF Contribution\". Such " +
"statements include oral statements in IETF sessi" +
"ons, as well as written and electronic communica" +
"tions made at any time or place, which are addressed to");
pbExpc = MemUtil.HexStringToByteArray(
"A3FBF07DF3FA2FDE4F376CA23E82737041605D9F4F4F57BD8CFF2C1D4B7955EC" +
"2A97948BD3722915C8F3D337F7D370050E9E96D647B7C39F56E031CA5EB6250D" +
"4042E02785ECECFA4B4BB5E8EAD0440E20B6E8DB09D881A7C6132F420E527950" +
"42BDFA7773D8A9051447B3291CE1411C680465552AA6C405B7764D5E87BEA85A" +
"D00F8449ED8F72D0D662AB052691CA66424BC86D2DF80EA41F43ABF937D3259D" +
"C4B2D0DFB48A6C9139DDD7F76966E928E635553BA76C5C879D7B35D49EB2E62B" +
"0871CDAC638939E25E8A1E0EF9D5280FA8CA328B351C3C765989CBCF3DAA8B6C" +
"CC3AAF9F3979C92B3720FC88DC95ED84A1BE059C6499B9FDA236E7E818B04B0B" +
"C39C1E876B193BFE5569753F88128CC08AAA9B63D1A16F80EF2554D7189C411F" +
"5869CA52C5B83FA36FF216B9C1D30062BEBCFD2DC5BCE0911934FDA79A86F6E6" +
"98CED759C3FF9B6477338F3DA4F9CD8514EA9982CCAFB341B2384DD902F3D1AB" +
"7AC61DD29C6F21BA5B862F3730E37CFDC4FD806C22F221");
using(MemoryStream msEnc = new MemoryStream())
{
using(ChaCha20Stream c = new ChaCha20Stream(msEnc, true, pbKey, pbIV))
{
r.NextBytes(pb64);
c.Write(pb64, 0, pb64.Length); // Skip first block
int p = 0;
while(p < pb.Length)
{
int cb = r.Next(1, pb.Length - p + 1);
c.Write(pb, p, cb);
p += cb;
}
Debug.Assert(p == pb.Length);
}
byte[] pbEnc0 = msEnc.ToArray();
byte[] pbEnc = MemUtil.Mid(pbEnc0, 64, pbEnc0.Length - 64);
if(!MemUtil.ArraysEqual(pbEnc, pbExpc))
throw new SecurityException("ChaCha20-3");
using(MemoryStream msCT = new MemoryStream(pbEnc0, false))
{
using(ChaCha20Stream cDec = new ChaCha20Stream(msCT, false,
pbKey, pbIV))
{
byte[] pbPT = MemUtil.Read(cDec, pbEnc0.Length);
if(cDec.ReadByte() >= 0)
throw new SecurityException("ChaCha20-4");
if(!MemUtil.ArraysEqual(MemUtil.Mid(pbPT, 0, 64), pb64))
throw new SecurityException("ChaCha20-5");
if(!MemUtil.ArraysEqual(MemUtil.Mid(pbPT, 64, pbEnc.Length), pb))
throw new SecurityException("ChaCha20-6");
}
}
}
// ======================================================
// Test vector TC8 from RFC draft by J. Strombergson:
// https://tools.ietf.org/html/draft-strombergson-chacha-test-vectors-01
pbKey = new byte[32] {
0xC4, 0x6E, 0xC1, 0xB1, 0x8C, 0xE8, 0xA8, 0x78,
0x72, 0x5A, 0x37, 0xE7, 0x80, 0xDF, 0xB7, 0x35,
0x1F, 0x68, 0xED, 0x2E, 0x19, 0x4C, 0x79, 0xFB,
0xC6, 0xAE, 0xBE, 0xE1, 0xA6, 0x67, 0x97, 0x5D
};
// The first 4 bytes are set to zero and a large counter
// is used; this makes the RFC 7539 version of ChaCha20
// compatible with the original specification by
// D. J. Bernstein.
pbIV = new byte[12] { 0x00, 0x00, 0x00, 0x00,
0x1A, 0xDA, 0x31, 0xD5, 0xCF, 0x68, 0x82, 0x21
};
pb = new byte[128];
pbExpc = new byte[128] {
0xF6, 0x3A, 0x89, 0xB7, 0x5C, 0x22, 0x71, 0xF9,
0x36, 0x88, 0x16, 0x54, 0x2B, 0xA5, 0x2F, 0x06,
0xED, 0x49, 0x24, 0x17, 0x92, 0x30, 0x2B, 0x00,
0xB5, 0xE8, 0xF8, 0x0A, 0xE9, 0xA4, 0x73, 0xAF,
0xC2, 0x5B, 0x21, 0x8F, 0x51, 0x9A, 0xF0, 0xFD,
0xD4, 0x06, 0x36, 0x2E, 0x8D, 0x69, 0xDE, 0x7F,
0x54, 0xC6, 0x04, 0xA6, 0xE0, 0x0F, 0x35, 0x3F,
0x11, 0x0F, 0x77, 0x1B, 0xDC, 0xA8, 0xAB, 0x92,
0xE5, 0xFB, 0xC3, 0x4E, 0x60, 0xA1, 0xD9, 0xA9,
0xDB, 0x17, 0x34, 0x5B, 0x0A, 0x40, 0x27, 0x36,
0x85, 0x3B, 0xF9, 0x10, 0xB0, 0x60, 0xBD, 0xF1,
0xF8, 0x97, 0xB6, 0x29, 0x0F, 0x01, 0xD1, 0x38,
0xAE, 0x2C, 0x4C, 0x90, 0x22, 0x5B, 0xA9, 0xEA,
0x14, 0xD5, 0x18, 0xF5, 0x59, 0x29, 0xDE, 0xA0,
0x98, 0xCA, 0x7A, 0x6C, 0xCF, 0xE6, 0x12, 0x27,
0x05, 0x3C, 0x84, 0xE4, 0x9A, 0x4A, 0x33, 0x32
};
using(ChaCha20Cipher c = new ChaCha20Cipher(pbKey, pbIV, true))
{
c.Decrypt(pb, 0, pb.Length);
if(!MemUtil.ArraysEqual(pb, pbExpc))
throw new SecurityException("ChaCha20-7");
}
#endif
}
private static void TestBlake2b(Random r)
{
#if !ModernKeePassLib && DEBUG
Blake2b h = new Blake2b();
// ======================================================
// From https://tools.ietf.org/html/rfc7693
byte[] pbData = StrUtil.Utf8.GetBytes("abc");
byte[] pbExpc = new byte[64] {
0xBA, 0x80, 0xA5, 0x3F, 0x98, 0x1C, 0x4D, 0x0D,
0x6A, 0x27, 0x97, 0xB6, 0x9F, 0x12, 0xF6, 0xE9,
0x4C, 0x21, 0x2F, 0x14, 0x68, 0x5A, 0xC4, 0xB7,
0x4B, 0x12, 0xBB, 0x6F, 0xDB, 0xFF, 0xA2, 0xD1,
0x7D, 0x87, 0xC5, 0x39, 0x2A, 0xAB, 0x79, 0x2D,
0xC2, 0x52, 0xD5, 0xDE, 0x45, 0x33, 0xCC, 0x95,
0x18, 0xD3, 0x8A, 0xA8, 0xDB, 0xF1, 0x92, 0x5A,
0xB9, 0x23, 0x86, 0xED, 0xD4, 0x00, 0x99, 0x23
};
byte[] pbC = h.ComputeHash(pbData);
if(!MemUtil.ArraysEqual(pbC, pbExpc))
throw new SecurityException("Blake2b-1");
// ======================================================
// Computed using the official b2sum tool
pbExpc = new byte[64] {
0x78, 0x6A, 0x02, 0xF7, 0x42, 0x01, 0x59, 0x03,
0xC6, 0xC6, 0xFD, 0x85, 0x25, 0x52, 0xD2, 0x72,
0x91, 0x2F, 0x47, 0x40, 0xE1, 0x58, 0x47, 0x61,
0x8A, 0x86, 0xE2, 0x17, 0xF7, 0x1F, 0x54, 0x19,
0xD2, 0x5E, 0x10, 0x31, 0xAF, 0xEE, 0x58, 0x53,
0x13, 0x89, 0x64, 0x44, 0x93, 0x4E, 0xB0, 0x4B,
0x90, 0x3A, 0x68, 0x5B, 0x14, 0x48, 0xB7, 0x55,
0xD5, 0x6F, 0x70, 0x1A, 0xFE, 0x9B, 0xE2, 0xCE
};
pbC = h.ComputeHash(MemUtil.EmptyByteArray);
if(!MemUtil.ArraysEqual(pbC, pbExpc))
throw new SecurityException("Blake2b-2");
// ======================================================
// Computed using the official b2sum tool
string strS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.:,;_-\r\n";
StringBuilder sb = new StringBuilder();
for(int i = 0; i < 1000; ++i) sb.Append(strS);
pbData = StrUtil.Utf8.GetBytes(sb.ToString());
pbExpc = new byte[64] {
0x59, 0x69, 0x8D, 0x3B, 0x83, 0xF4, 0x02, 0x4E,
0xD8, 0x99, 0x26, 0x0E, 0xF4, 0xE5, 0x9F, 0x20,
0xDC, 0x31, 0xEE, 0x5B, 0x45, 0xEA, 0xBB, 0xFC,
0x1C, 0x0A, 0x8E, 0xED, 0xAA, 0x7A, 0xFF, 0x50,
0x82, 0xA5, 0x8F, 0xBC, 0x4A, 0x46, 0xFC, 0xC5,
0xEF, 0x44, 0x4E, 0x89, 0x80, 0x7D, 0x3F, 0x1C,
0xC1, 0x94, 0x45, 0xBB, 0xC0, 0x2C, 0x95, 0xAA,
0x3F, 0x08, 0x8A, 0x93, 0xF8, 0x75, 0x91, 0xB0
};
int p = 0;
while(p < pbData.Length)
{
int cb = r.Next(1, pbData.Length - p + 1);
h.TransformBlock(pbData, p, cb, pbData, p);
p += cb;
}
Debug.Assert(p == pbData.Length);
h.TransformFinalBlock(MemUtil.EmptyByteArray, 0, 0);
if(!MemUtil.ArraysEqual(h.Hash, pbExpc))
throw new SecurityException("Blake2b-3");
h.Clear();
#endif
}
private static void TestArgon2()
{
#if DEBUG
Argon2Kdf kdf = new Argon2Kdf();
// ======================================================
// From the official Argon2 1.3 reference code package
// (test vector for Argon2d 1.3); also on
// https://tools.ietf.org/html/draft-irtf-cfrg-argon2-00
var p = kdf.GetDefaultParameters();
kdf.Randomize(p);
Debug.Assert(p.GetUInt32(Argon2Kdf.ParamVersion, 0) == 0x13U);
byte[] pbMsg = new byte[32];
for(int i = 0; i < pbMsg.Length; ++i) pbMsg[i] = 1;
p.SetUInt64(Argon2Kdf.ParamMemory, 32 * 1024);
p.SetUInt64(Argon2Kdf.ParamIterations, 3);
p.SetUInt32(Argon2Kdf.ParamParallelism, 4);
byte[] pbSalt = new byte[16];
for(int i = 0; i < pbSalt.Length; ++i) pbSalt[i] = 2;
p.SetByteArray(Argon2Kdf.ParamSalt, pbSalt);
byte[] pbKey = new byte[8];
for(int i = 0; i < pbKey.Length; ++i) pbKey[i] = 3;
p.SetByteArray(Argon2Kdf.ParamSecretKey, pbKey);
byte[] pbAssoc = new byte[12];
for(int i = 0; i < pbAssoc.Length; ++i) pbAssoc[i] = 4;
p.SetByteArray(Argon2Kdf.ParamAssocData, pbAssoc);
byte[] pbExpc = new byte[32] {
0x51, 0x2B, 0x39, 0x1B, 0x6F, 0x11, 0x62, 0x97,
0x53, 0x71, 0xD3, 0x09, 0x19, 0x73, 0x42, 0x94,
0xF8, 0x68, 0xE3, 0xBE, 0x39, 0x84, 0xF3, 0xC1,
0xA1, 0x3A, 0x4D, 0xB9, 0xFA, 0xBE, 0x4A, 0xCB
};
byte[] pb = kdf.Transform(pbMsg, p);
if(!MemUtil.ArraysEqual(pb, pbExpc))
throw new SecurityException("Argon2-1");
// ======================================================
// From the official Argon2 1.3 reference code package
// (test vector for Argon2d 1.0)
p.SetUInt32(Argon2Kdf.ParamVersion, 0x10);
pbExpc = new byte[32] {
0x96, 0xA9, 0xD4, 0xE5, 0xA1, 0x73, 0x40, 0x92,
0xC8, 0x5E, 0x29, 0xF4, 0x10, 0xA4, 0x59, 0x14,
0xA5, 0xDD, 0x1F, 0x5C, 0xBF, 0x08, 0xB2, 0x67,
0x0D, 0xA6, 0x8A, 0x02, 0x85, 0xAB, 0xF3, 0x2B
};
pb = kdf.Transform(pbMsg, p);
if(!MemUtil.ArraysEqual(pb, pbExpc))
throw new SecurityException("Argon2-2");
// ======================================================
// From the official 'phc-winner-argon2-20151206.zip'
// (test vector for Argon2d 1.0)
p.SetUInt64(Argon2Kdf.ParamMemory, 16 * 1024);
pbExpc = new byte[32] {
0x57, 0xB0, 0x61, 0x3B, 0xFD, 0xD4, 0x13, 0x1A,
0x0C, 0x34, 0x88, 0x34, 0xC6, 0x72, 0x9C, 0x2C,
0x72, 0x29, 0x92, 0x1E, 0x6B, 0xBA, 0x37, 0x66,
0x5D, 0x97, 0x8C, 0x4F, 0xE7, 0x17, 0x5E, 0xD2
};
pb = kdf.Transform(pbMsg, p);
if(!MemUtil.ArraysEqual(pb, pbExpc))
throw new SecurityException("Argon2-3");
#if SELFTEST_ARGON2_LONG
// ======================================================
// Computed using the official 'argon2' application
// (test vectors for Argon2d 1.3)
p = kdf.GetDefaultParameters();
pbMsg = StrUtil.Utf8.GetBytes("ABC1234");
p.SetUInt64(Argon2Kdf.ParamMemory, (1 << 11) * 1024); // 2 MB
p.SetUInt64(Argon2Kdf.ParamIterations, 2);
p.SetUInt32(Argon2Kdf.ParamParallelism, 2);
pbSalt = StrUtil.Utf8.GetBytes("somesalt");
p.SetByteArray(Argon2Kdf.ParamSalt, pbSalt);
pbExpc = new byte[32] {
0x29, 0xCB, 0xD3, 0xA1, 0x93, 0x76, 0xF7, 0xA2,
0xFC, 0xDF, 0xB0, 0x68, 0xAC, 0x0B, 0x99, 0xBA,
0x40, 0xAC, 0x09, 0x01, 0x73, 0x42, 0xCE, 0xF1,
0x29, 0xCC, 0xA1, 0x4F, 0xE1, 0xC1, 0xB7, 0xA3
};
pb = kdf.Transform(pbMsg, p);
if(!MemUtil.ArraysEqual(pb, pbExpc))
throw new SecurityException("Argon2-4");
p.SetUInt64(Argon2Kdf.ParamMemory, (1 << 10) * 1024); // 1 MB
p.SetUInt64(Argon2Kdf.ParamIterations, 3);
pbExpc = new byte[32] {
0x7A, 0xBE, 0x1C, 0x1C, 0x8D, 0x7F, 0xD6, 0xDC,
0x7C, 0x94, 0x06, 0x3E, 0xD8, 0xBC, 0xD8, 0x1C,
0x2F, 0x87, 0x84, 0x99, 0x12, 0x83, 0xFE, 0x76,
0x00, 0x64, 0xC4, 0x58, 0xA4, 0xDA, 0x35, 0x70
};
pb = kdf.Transform(pbMsg, p);
if(!MemUtil.ArraysEqual(pb, pbExpc))
throw new SecurityException("Argon2-5");
#if SELFTEST_ARGON2_LONGER
p.SetUInt64(Argon2Kdf.ParamMemory, (1 << 20) * 1024); // 1 GB
p.SetUInt64(Argon2Kdf.ParamIterations, 2);
p.SetUInt32(Argon2Kdf.ParamParallelism, 3);
pbExpc = new byte[32] {
0xE6, 0xE7, 0xCB, 0xF5, 0x5A, 0x06, 0x93, 0x05,
0x32, 0xBA, 0x86, 0xC6, 0x1F, 0x45, 0x17, 0x99,
0x65, 0x41, 0x77, 0xF9, 0x30, 0x55, 0x9A, 0xE8,
0x3D, 0x21, 0x48, 0xC6, 0x2D, 0x0C, 0x49, 0x11
};
pb = kdf.Transform(pbMsg, p);
if(!MemUtil.ArraysEqual(pb, pbExpc))
throw new SecurityException("Argon2-6");
#endif // SELFTEST_ARGON2_LONGER
#endif // SELFTEST_ARGON2_LONG
#endif // DEBUG
}
private static void TestHmac()
{
#if DEBUG
// Test vectors from RFC 4231
byte[] pbKey = new byte[20];
for(int i = 0; i < pbKey.Length; ++i) pbKey[i] = 0x0B;
byte[] pbMsg = StrUtil.Utf8.GetBytes("Hi There");
byte[] pbExpc = new byte[32] {
0xB0, 0x34, 0x4C, 0x61, 0xD8, 0xDB, 0x38, 0x53,
0x5C, 0xA8, 0xAF, 0xCE, 0xAF, 0x0B, 0xF1, 0x2B,
0x88, 0x1D, 0xC2, 0x00, 0xC9, 0x83, 0x3D, 0xA7,
0x26, 0xE9, 0x37, 0x6C, 0x2E, 0x32, 0xCF, 0xF7
};
HmacEval(pbKey, pbMsg, pbExpc, "1");
pbKey = new byte[131];
for(int i = 0; i < pbKey.Length; ++i) pbKey[i] = 0xAA;
pbMsg = StrUtil.Utf8.GetBytes(
"This is a test using a larger than block-size key and " +
"a larger than block-size data. The key needs to be " +
"hashed before being used by the HMAC algorithm.");
pbExpc = new byte[32] {
0x9B, 0x09, 0xFF, 0xA7, 0x1B, 0x94, 0x2F, 0xCB,
0x27, 0x63, 0x5F, 0xBC, 0xD5, 0xB0, 0xE9, 0x44,
0xBF, 0xDC, 0x63, 0x64, 0x4F, 0x07, 0x13, 0x93,
0x8A, 0x7F, 0x51, 0x53, 0x5C, 0x3A, 0x35, 0xE2
};
HmacEval(pbKey, pbMsg, pbExpc, "2");
#endif
}
#if DEBUG
private static void HmacEval(byte[] pbKey, byte[] pbMsg,
byte[] pbExpc, string strID)
{
// WinRT
#if ModernKeePassLib
var h = MacAlgorithmProvider.OpenAlgorithm(MacAlgorithmNames.HmacSha256).CreateHash(CryptographicBuffer.CreateFromByteArray(pbKey));
h.Append(CryptographicBuffer.CreateFromByteArray(pbMsg));
var pbHash = h.GetValueAndReset().ToArray();
if (!MemUtil.ArraysEqual(pbHash, pbExpc))
throw new SecurityException("HMAC-SHA-256-" + strID);
h.Append(CryptographicBuffer.CreateFromByteArray(pbMsg));
pbHash = h.GetValueAndReset().ToArray();
if (!MemUtil.ArraysEqual(pbHash, pbExpc))
throw new SecurityException("HMAC-SHA-256-" + strID + "-R");
// BouncyCastle
/*var h = new HMac(new Sha256Digest());
h.BlockUpdate(pbMsg, 0, pbMsg.Length);
byte[] pbHash = MemUtil.EmptyByteArray;
h.DoFinal(pbHash, 0);
if (!MemUtil.ArraysEqual(pbHash, pbExpc))
throw new SecurityException("HMAC-SHA-256-" + strID);
h.Reset();
h.BlockUpdate(pbMsg, 0, pbMsg.Length);
h.DoFinal(pbHash, 0);
if (!MemUtil.ArraysEqual(pbHash, pbExpc))
throw new SecurityException("HMAC-SHA-256-" + strID + "-R");*/
#else
// Original
using(HMACSHA256 h = new HMACSHA256(pbKey))
{
h.TransformBlock(pbMsg, 0, pbMsg.Length, pbMsg, 0);
h.TransformFinalBlock(MemUtil.EmptyByteArray, 0, 0);
byte[] pbHash = h.Hash;
if(!MemUtil.ArraysEqual(pbHash, pbExpc))
throw new SecurityException("HMAC-SHA-256-" + strID);
// Reuse the object
h.Initialize();
h.TransformBlock(pbMsg, 0, pbMsg.Length, pbMsg, 0);
h.TransformFinalBlock(MemUtil.EmptyByteArray, 0, 0);
pbHash = h.Hash;
if(!MemUtil.ArraysEqual(pbHash, pbExpc))
throw new SecurityException("HMAC-SHA-256-" + strID + "-R");
}
#endif
}
#endif
private static void TestKeyTransform(Random r)
{
#if DEBUG
// Up to KeePass 2.34, the OtpKeyProv plugin used the public
// CompositeKey.TransformKeyManaged method (and a finalizing
// SHA-256 computation), which became an internal method of
// the AesKdf class in KeePass 2.35, thus OtpKeyProv now
// uses the AesKdf class; here we ensure that the results
// are the same
byte[] pbKey = new byte[32];
r.NextBytes(pbKey);
byte[] pbSeed = new byte[32];
r.NextBytes(pbSeed);
ulong uRounds = (ulong)r.Next(1, 0x7FFF);
byte[] pbMan = new byte[pbKey.Length];
Array.Copy(pbKey, pbMan, pbKey.Length);
if(!AesKdf.TransformKeyManaged(pbMan, pbSeed, uRounds))
throw new SecurityException("AES-KDF-1");
pbMan = CryptoUtil.HashSha256(pbMan);
AesKdf kdf = new AesKdf();
var p = kdf.GetDefaultParameters();
p.SetUInt64(AesKdf.ParamRounds, uRounds);
p.SetByteArray(AesKdf.ParamSeed, pbSeed);
byte[] pbKdf = kdf.Transform(pbKey, p);
if(!MemUtil.ArraysEqual(pbMan, pbKdf))
throw new SecurityException("AES-KDF-2");
#endif
}
#if !ModernKeePassLib #if !ModernKeePassLib
private static void TestNativeKeyTransform() private static void TestNativeKeyTransform()
{ {
#if DEBUG #if DEBUG
byte[] pbOrgKey = CryptoRandom.Instance.GetRandomBytes(32); byte[] pbOrgKey = CryptoRandom.Instance.GetRandomBytes(32);
byte[] pbSeed = CryptoRandom.Instance.GetRandomBytes(32); byte[] pbSeed = CryptoRandom.Instance.GetRandomBytes(32);
ulong uRounds = (ulong)((new Random()).Next(1, 0x3FFF)); ulong uRounds = (ulong)r.Next(1, 0x3FFF);
byte[] pbManaged = new byte[32]; byte[] pbManaged = new byte[32];
Array.Copy(pbOrgKey, pbManaged, 32); Array.Copy(pbOrgKey, pbManaged, 32);
if(CompositeKey.TransformKeyManaged(pbManaged, pbSeed, uRounds) == false) if(!AesKdf.TransformKeyManaged(pbManaged, pbSeed, uRounds))
throw new SecurityException("Managed transform."); throw new SecurityException("AES-KDF-1");
byte[] pbNative = new byte[32]; byte[] pbNative = new byte[32];
Array.Copy(pbOrgKey, pbNative, 32); Array.Copy(pbOrgKey, pbNative, 32);
if(NativeLib.TransformKey256(pbNative, pbSeed, uRounds) == false) if(!NativeLib.TransformKey256(pbNative, pbSeed, uRounds))
return; // Native library not available ("success") return; // Native library not available ("success")
if(!MemUtil.ArraysEqual(pbManaged, pbNative)) if(!MemUtil.ArraysEqual(pbManaged, pbNative))
throw new SecurityException("Native transform."); throw new SecurityException("AES-KDF-2");
#endif #endif
} }
#endif #endif
private static void TestMemUtil() private static void TestMemUtil(Random r)
{ {
#if DEBUG #if DEBUG
Random r = new Random();
byte[] pb = CryptoRandom.Instance.GetRandomBytes((uint)r.Next( byte[] pb = CryptoRandom.Instance.GetRandomBytes((uint)r.Next(
0, 0x2FFFF)); 0, 0x2FFFF));
@@ -297,12 +855,21 @@ namespace ModernKeePassLib.Cryptography
pbRes = MemUtil.ParseBase32("JNSXSIDQOJXXM2LEMVZCAYTBONSWIIDPNYQG63TFFV2GS3LFEBYGC43TO5XXEZDTFY======"); pbRes = MemUtil.ParseBase32("JNSXSIDQOJXXM2LEMVZCAYTBONSWIIDPNYQG63TFFV2GS3LFEBYGC43TO5XXEZDTFY======");
pbExp = Encoding.UTF8.GetBytes("Key provider based on one-time passwords."); pbExp = Encoding.UTF8.GetBytes("Key provider based on one-time passwords.");
if(!MemUtil.ArraysEqual(pbRes, pbExp)) throw new Exception("Base32-7"); if(!MemUtil.ArraysEqual(pbRes, pbExp)) throw new Exception("Base32-7");
int i = 0 - 0x10203040;
pbRes = MemUtil.Int32ToBytes(i);
if(MemUtil.ByteArrayToHexString(pbRes) != "C0CFDFEF")
throw new Exception("MemUtil-8"); // Must be little-endian
if(MemUtil.BytesToUInt32(pbRes) != (uint)i)
throw new Exception("MemUtil-9");
if(MemUtil.BytesToInt32(pbRes) != i)
throw new Exception("MemUtil-10");
#endif #endif
} }
private static void TestHmacOtp() private static void TestHmacOtp()
{ {
#if (DEBUG && !KeePassLibSD && !KeePassRT) #if (DEBUG && !KeePassLibSD)
byte[] pbSecret = StrUtil.Utf8.GetBytes("12345678901234567890"); byte[] pbSecret = StrUtil.Utf8.GetBytes("12345678901234567890");
string[] vExp = new string[]{ "755224", "287082", "359152", string[] vExp = new string[]{ "755224", "287082", "359152",
"969429", "338314", "254676", "287922", "162583", "399871", "969429", "338314", "254676", "287922", "162583", "399871",
@@ -316,7 +883,7 @@ namespace ModernKeePassLib.Cryptography
#endif #endif
} }
private static void TestProtectedObjects() private static void TestProtectedObjects(Random r)
{ {
#if DEBUG #if DEBUG
Encoding enc = StrUtil.Utf8; Encoding enc = StrUtil.Utf8;
@@ -371,7 +938,6 @@ namespace ModernKeePassLib.Cryptography
if(!ps.IsProtected) throw new SecurityException("ProtectedString-9"); if(!ps.IsProtected) throw new SecurityException("ProtectedString-9");
if(!ps2.IsProtected) throw new SecurityException("ProtectedString-10"); if(!ps2.IsProtected) throw new SecurityException("ProtectedString-10");
Random r = new Random();
string str = string.Empty; string str = string.Empty;
ps = new ProtectedString(); ps = new ProtectedString();
for(int i = 0; i < 100; ++i) for(int i = 0; i < 100; ++i)
@@ -497,16 +1063,26 @@ namespace ModernKeePassLib.Cryptography
if(short.MinValue.ToString(NumberFormatInfo.InvariantInfo) != if(short.MinValue.ToString(NumberFormatInfo.InvariantInfo) !=
"-32768") "-32768")
throw new InvalidOperationException("StrUtil-Inv4"); throw new InvalidOperationException("StrUtil-Inv4");
if(!string.Equals("abcd", "aBcd", StrUtil.CaseIgnoreCmp))
throw new InvalidOperationException("StrUtil-Case1");
if(string.Equals(@"a<b", @"a>b", StrUtil.CaseIgnoreCmp))
throw new InvalidOperationException("StrUtil-Case2");
#endif #endif
} }
private static void TestUrlUtil() private static void TestUrlUtil()
{ {
#if DEBUG #if DEBUG
#if !ModernKeePassLib
Debug.Assert(Uri.UriSchemeHttp.Equals("http", StrUtil.CaseIgnoreCmp));
Debug.Assert(Uri.UriSchemeHttps.Equals("https", StrUtil.CaseIgnoreCmp));
#endif
if(UrlUtil.GetHost(@"scheme://domain:port/path?query_string#fragment_id") != if(UrlUtil.GetHost(@"scheme://domain:port/path?query_string#fragment_id") !=
"domain") "domain")
throw new InvalidOperationException("UrlUtil-H1"); throw new InvalidOperationException("UrlUtil-H1");
if(UrlUtil.GetHost(@"http://example.org:80") != "example.org") if(UrlUtil.GetHost(@"https://example.org:443") != "example.org")
throw new InvalidOperationException("UrlUtil-H2"); throw new InvalidOperationException("UrlUtil-H2");
if(UrlUtil.GetHost(@"mailto:bob@example.com") != "example.com") if(UrlUtil.GetHost(@"mailto:bob@example.com") != "example.com")
throw new InvalidOperationException("UrlUtil-H3"); throw new InvalidOperationException("UrlUtil-H3");

View File

@@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2014 Dominik Reichl <dominik.reichl@t-online.de> Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by

View File

@@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2014 Dominik Reichl <dominik.reichl@t-online.de> Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by

View File

@@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2014 Dominik Reichl <dominik.reichl@t-online.de> Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by

View File

@@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2014 Dominik Reichl <dominik.reichl@t-online.de> Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by

View File

@@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2014 Dominik Reichl <dominik.reichl@t-online.de> Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by

View File

@@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2014 Dominik Reichl <dominik.reichl@t-online.de> Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by

View File

@@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2014 Dominik Reichl <dominik.reichl@t-online.de> Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by

View File

@@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2014 Dominik Reichl <dominik.reichl@t-online.de> Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -40,8 +40,10 @@ using ModernKeePassLib.Security;
using ModernKeePassLib.Utility; using ModernKeePassLib.Utility;
using Windows.Security.Cryptography.Core; using Windows.Security.Cryptography.Core;
using Windows.Storage.Streams; using Windows.Storage.Streams;
using ModernKeePassLib.Cryptography;
using ModernKeePassLib.Cryptography.KeyDerivation;
using Org.BouncyCastle.Crypto.Engines; using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Parameters; using KdfParameters = Org.BouncyCastle.Crypto.Parameters.KdfParameters;
namespace ModernKeePassLib.Keys namespace ModernKeePassLib.Keys
{ {
@@ -133,8 +135,15 @@ namespace ModernKeePassLib.Keys
foreach(IUserKey pKey in m_vUserKeys) foreach(IUserKey pKey in m_vUserKeys)
{ {
if(pKey == null) { Debug.Assert(false); continue; }
#if KeePassUAP
if(pKey.GetType() == tUserKeyType)
return true;
#else
if(tUserKeyType.IsInstanceOfType(pKey)) if(tUserKeyType.IsInstanceOfType(pKey))
return true; return true;
#endif
} }
return false; return false;
@@ -153,8 +162,15 @@ namespace ModernKeePassLib.Keys
foreach(IUserKey pKey in m_vUserKeys) foreach(IUserKey pKey in m_vUserKeys)
{ {
if(pKey == null) { Debug.Assert(false); continue; }
#if KeePassUAP
if(pKey.GetType() == tUserKeyType)
return pKey;
#else
if(tUserKeyType.IsInstanceOfType(pKey)) if(tUserKeyType.IsInstanceOfType(pKey))
return pKey; return pKey;
#endif
} }
return null; return null;
@@ -170,30 +186,31 @@ namespace ModernKeePassLib.Keys
ValidateUserKeys(); ValidateUserKeys();
// Concatenate user key data // Concatenate user key data
MemoryStream ms = new MemoryStream(); List<byte[]> lData = new List<byte[]>();
int cbData = 0;
foreach(IUserKey pKey in m_vUserKeys) foreach(IUserKey pKey in m_vUserKeys)
{ {
ProtectedBinary b = pKey.KeyData; ProtectedBinary b = pKey.KeyData;
if(b != null) if(b != null)
{ {
byte[] pbKeyData = b.ReadData(); byte[] pbKeyData = b.ReadData();
ms.Write(pbKeyData, 0, pbKeyData.Length); lData.Add(pbKeyData);
MemUtil.ZeroByteArray(pbKeyData); cbData += pbKeyData.Length;
} }
} }
#if ModernKeePassLib byte[] pbAllData = new byte[cbData];
/*var sha256 = WinRTCrypto.HashAlgorithmProvider.OpenAlgorithm(HashAlgorithm.Sha256); int p = 0;
var pbHash = sha256.HashData(ms.ToArray());*/ foreach(byte[] pbData in lData)
var sha256 = HashAlgorithmProvider.OpenAlgorithm(HashAlgorithmNames.Sha256); {
var buffer = sha256.HashData(CryptographicBuffer.CreateFromByteArray(ms.ToArray())); Array.Copy(pbData, 0, pbAllData, p, pbData.Length);
byte[] pbHash; p += pbData.Length;
CryptographicBuffer.CopyToByteArray(buffer, out pbHash); MemUtil.ZeroByteArray(pbData);
#else }
SHA256Managed sha256 = new SHA256Managed(); Debug.Assert(p == cbData);
byte[] pbHash = sha256.ComputeHash(ms.ToArray());
#endif byte[] pbHash = CryptoUtil.HashSha256(pbAllData);
ms.Dispose(); MemUtil.ZeroByteArray(pbAllData);
return pbHash; return pbHash;
} }
@@ -204,21 +221,13 @@ namespace ModernKeePassLib.Keys
byte[] pbThis = CreateRawCompositeKey32(); byte[] pbThis = CreateRawCompositeKey32();
byte[] pbOther = ckOther.CreateRawCompositeKey32(); byte[] pbOther = ckOther.CreateRawCompositeKey32();
bool bResult = MemUtil.ArraysEqual(pbThis, pbOther); bool bResult = MemUtil.ArraysEqual(pbThis, pbOther);
Array.Clear(pbOther, 0, pbOther.Length); MemUtil.ZeroByteArray(pbOther);
Array.Clear(pbThis, 0, pbThis.Length); MemUtil.ZeroByteArray(pbThis);
return bResult; return bResult;
} }
/// <summary> [Obsolete]
/// Generate a 32-bit wide key out of the composite key.
/// </summary>
/// <param name="pbKeySeed32">Seed used in the key transformation
/// rounds. Must be a byte array containing exactly 32 bytes; must
/// not be null.</param>
/// <param name="uNumRounds">Number of key transformation rounds.</param>
/// <returns>Returns a protected binary object that contains the
/// resulting 32-bit wide key.</returns>
public ProtectedBinary GenerateKey32(byte[] pbKeySeed32, ulong uNumRounds) public ProtectedBinary GenerateKey32(byte[] pbKeySeed32, ulong uNumRounds)
{ {
Debug.Assert(pbKeySeed32 != null); Debug.Assert(pbKeySeed32 != null);
@@ -226,24 +235,48 @@ namespace ModernKeePassLib.Keys
Debug.Assert(pbKeySeed32.Length == 32); Debug.Assert(pbKeySeed32.Length == 32);
if(pbKeySeed32.Length != 32) throw new ArgumentException("pbKeySeed32"); if(pbKeySeed32.Length != 32) throw new ArgumentException("pbKeySeed32");
AesKdf kdf = new AesKdf();
var 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(Cryptography.KeyDerivation.KdfParameters p)
{
if(p == null) { Debug.Assert(false); throw new ArgumentNullException("p"); }
byte[] pbRaw32 = CreateRawCompositeKey32(); byte[] pbRaw32 = CreateRawCompositeKey32();
if((pbRaw32 == null) || (pbRaw32.Length != 32)) if((pbRaw32 == null) || (pbRaw32.Length != 32))
{ Debug.Assert(false); return null; } { Debug.Assert(false); return null; }
byte[] pbTrf32 = TransformKey(pbRaw32, pbKeySeed32, uNumRounds); KdfEngine kdf = KdfPool.Get(p.KdfUuid);
if((pbTrf32 == null) || (pbTrf32.Length != 32)) if(kdf == null) // CryptographicExceptions are translated to "file corrupted"
{ Debug.Assert(false); return null; } throw new Exception(KLRes.UnknownKdf + Environment.NewLine +
KLRes.FileNewVerOrPlgReq + Environment.NewLine +
"UUID: " + p.KdfUuid.ToHexString() + ".");
byte[] pbTrf32 = kdf.Transform(pbRaw32, p);
if(pbTrf32 == null) { Debug.Assert(false); return null; }
if(pbTrf32.Length != 32)
{
Debug.Assert(false);
pbTrf32 = CryptoUtil.HashSha256(pbTrf32);
}
ProtectedBinary pbRet = new ProtectedBinary(true, pbTrf32); ProtectedBinary pbRet = new ProtectedBinary(true, pbTrf32);
MemUtil.ZeroByteArray(pbTrf32); MemUtil.ZeroByteArray(pbTrf32);
MemUtil.ZeroByteArray(pbRaw32); MemUtil.ZeroByteArray(pbRaw32);
return pbRet; return pbRet;
} }
private void ValidateUserKeys() private void ValidateUserKeys()
{ {
#if !ModernKeePassLib
int nAccounts = 0; int nAccounts = 0;
foreach(IUserKey uKey in m_vUserKeys) foreach(IUserKey uKey in m_vUserKeys)
@@ -257,223 +290,7 @@ namespace ModernKeePassLib.Keys
Debug.Assert(false); Debug.Assert(false);
throw new InvalidOperationException(); throw new InvalidOperationException();
} }
#endif
} }
/// <summary>
/// Transform the current key <c>uNumRounds</c> times.
/// </summary>
/// <param name="pbOriginalKey32">The original key which will be transformed.
/// This parameter won't be modified.</param>
/// <param name="pbKeySeed32">Seed used for key transformations. Must not
/// be <c>null</c>. This parameter won't be modified.</param>
/// <param name="uNumRounds">Transformation count.</param>
/// <returns>256-bit transformed key.</returns>
private static byte[] TransformKey(byte[] pbOriginalKey32, byte[] pbKeySeed32,
ulong uNumRounds)
{
Debug.Assert((pbOriginalKey32 != null) && (pbOriginalKey32.Length == 32));
if(pbOriginalKey32 == null) throw new ArgumentNullException("pbOriginalKey32");
if(pbOriginalKey32.Length != 32) throw new ArgumentException();
Debug.Assert((pbKeySeed32 != null) && (pbKeySeed32.Length == 32));
if(pbKeySeed32 == null) throw new ArgumentNullException("pbKeySeed32");
if(pbKeySeed32.Length != 32) throw new ArgumentException();
byte[] pbNewKey = new byte[32];
Array.Copy(pbOriginalKey32, pbNewKey, pbNewKey.Length);
#if !ModernKeePassLib
// Try to use the native library first
if(NativeLib.TransformKey256(pbNewKey, pbKeySeed32, uNumRounds))
return (new SHA256Managed()).ComputeHash(pbNewKey);
#endif
if(TransformKeyManaged(ref pbNewKey, pbKeySeed32, uNumRounds) == false)
return null;
#if ModernKeePassLib
/*var sha256 = WinRTCrypto.HashAlgorithmProvider.OpenAlgorithm(HashAlgorithm.Sha256);
return sha256.HashData(pbNewKey);*/
var sha256 = HashAlgorithmProvider.OpenAlgorithm(HashAlgorithmNames.Sha256);
byte[] result;
var buffer = sha256.HashData(CryptographicBuffer.CreateFromByteArray(pbNewKey));
CryptographicBuffer.CopyToByteArray(buffer, out result);
return result;
#else
SHA256Managed sha256 = new SHA256Managed();
return sha256.ComputeHash(pbNewKey);
#endif
}
public static bool TransformKeyManaged(ref byte[] pbNewKey32, byte[] pbKeySeed32,
ulong uNumRounds)
{
#if KeePassRT
KeyParameter kp = new KeyParameter(pbKeySeed32);
AesEngine aes = new AesEngine();
aes.Init(true, kp);
for(ulong i = 0; i < uNumRounds; ++i)
{
aes.ProcessBlock(pbNewKey32, 0, pbNewKey32, 0);
aes.ProcessBlock(pbNewKey32, 16, pbNewKey32, 16);
}
#else
#if ModernKeePassLib
/*var aes = WinRTCrypto.SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithm.AesEcb);
var key = aes.CreateSymmetricKey(pbKeySeed32);
var iCrypt = WinRTCrypto.CryptographicEngine.CreateEncryptor(key);*/
/*var aes = SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithmNames.AesEcb);
var key = aes.CreateSymmetricKey(CryptographicBuffer.CreateFromByteArray(pbKeySeed32));
var parameters = KeyDerivationParameters.BuildForPbkdf2(CryptographicBuffer.CreateFromByteArray(pbKeySeed32), (uint)uNumRounds);
var result = CryptographicEngine.DeriveKeyMaterial(key, parameters, 32);
CryptographicBuffer.CopyToByteArray(result, out pbNewKey32);*/
KeyParameter kp = new KeyParameter(pbKeySeed32);
AesEngine aes = new AesEngine();
aes.Init(true, kp);
for (ulong i = 0; i < uNumRounds; ++i)
{
aes.ProcessBlock(pbNewKey32, 0, pbNewKey32, 0);
aes.ProcessBlock(pbNewKey32, 16, pbNewKey32, 16);
}
#else
byte[] pbIV = new byte[16];
Array.Clear(pbIV, 0, pbIV.Length);
RijndaelManaged r = new RijndaelManaged();
if(r.BlockSize != 128) // AES block size
{
Debug.Assert(false);
r.BlockSize = 128;
}
r.IV = pbIV;
r.Mode = CipherMode.ECB;
r.KeySize = 256;
r.Key = pbKeySeed32;
ICryptoTransform iCrypt = r.CreateEncryptor();
#endif
// !iCrypt.CanReuseTransform -- doesn't work with Mono
/*if ((iCrypt == null) || (iCrypt.InputBlockSize != 16) ||
(iCrypt.OutputBlockSize != 16))
{
Debug.Assert(false, "Invalid ICryptoTransform.");
Debug.Assert((iCrypt.InputBlockSize == 16), "Invalid input block size!");
Debug.Assert((iCrypt.OutputBlockSize == 16), "Invalid output block size!");
return false;
}
for(ulong i = 0; i < uNumRounds; ++i)
{
iCrypt.TransformBlock(pbNewKey32, 0, 16, pbNewKey32, 0);
iCrypt.TransformBlock(pbNewKey32, 16, 16, pbNewKey32, 16);
}*/
#endif
return true;
}
/// <summary>
/// Benchmark the <c>TransformKey</c> method. Within
/// <paramref name="uMilliseconds"/> ms, random keys will be transformed
/// and the number of performed transformations are returned.
/// </summary>
/// <param name="uMilliseconds">Test duration in ms.</param>
/// <param name="uStep">Stepping.
/// <paramref name="uStep" /> should be a prime number. For fast processors
/// (PCs) a value of <c>3001</c> is recommended, for slower processors (PocketPC)
/// a value of <c>401</c> is recommended.</param>
/// <returns>Number of transformations performed in the specified
/// amount of time. Maximum value is <c>uint.MaxValue</c>.</returns>
/*public static ulong TransformKeyBenchmark(uint uMilliseconds, ulong uStep)
{
ulong uRounds;
#if !ModernKeePassLib
// Try native method
if(NativeLib.TransformKeyBenchmark256(uMilliseconds, out uRounds))
return uRounds;
#endif
byte[] pbKey = new byte[32];
byte[] pbNewKey = new byte[32];
for(int i = 0; i < pbKey.Length; ++i)
{
pbKey[i] = (byte)i;
pbNewKey[i] = (byte)i;
}
#if KeePassRT
KeyParameter kp = new KeyParameter(pbKey);
AesEngine aes = new AesEngine();
aes.Init(true, kp);
#else
#if ModernKeePassLib
var aes = WinRTCrypto.SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithm.AesEcb);
var key = aes.CreateSymmetricKey(pbKey);
var iCrypt = WinRTCrypto.CryptographicEngine.CreateEncryptor(key);
#else
byte[] pbIV = new byte[16];
Array.Clear(pbIV, 0, pbIV.Length);
RijndaelManaged r = new RijndaelManaged();
if(r.BlockSize != 128) // AES block size
{
Debug.Assert(false);
r.BlockSize = 128;
}
r.IV = pbIV;
r.Mode = CipherMode.ECB;
r.KeySize = 256;
r.Key = pbKey;
ICryptoTransform iCrypt = r.CreateEncryptor();
#endif
// !iCrypt.CanReuseTransform -- doesn't work with Mono
if ((iCrypt == null) || (iCrypt.InputBlockSize != 16) ||
(iCrypt.OutputBlockSize != 16))
{
Debug.Assert(false, "Invalid ICryptoTransform.");
Debug.Assert(iCrypt.InputBlockSize == 16, "Invalid input block size!");
Debug.Assert(iCrypt.OutputBlockSize == 16, "Invalid output block size!");
return PwDefs.DefaultKeyEncryptionRounds;
}
#endif
uRounds = 0;
int tStart = Environment.TickCount;
while(true)
{
for(ulong j = 0; j < uStep; ++j)
{
#if KeePassRT
aes.ProcessBlock(pbNewKey, 0, pbNewKey, 0);
aes.ProcessBlock(pbNewKey, 16, pbNewKey, 16);
#else
iCrypt.TransformBlock(pbNewKey, 0, 16, pbNewKey, 0);
iCrypt.TransformBlock(pbNewKey, 16, 16, pbNewKey, 16);
#endif
}
uRounds += uStep;
if(uRounds < uStep) // Overflow check
{
uRounds = ulong.MaxValue;
break;
}
uint tElapsed = (uint)(Environment.TickCount - tStart);
if(tElapsed > uMilliseconds) break;
}
return uRounds;
}*/
} }
public sealed class InvalidCompositeKeyException : Exception public sealed class InvalidCompositeKeyException : Exception
@@ -482,8 +299,8 @@ namespace ModernKeePassLib.Keys
{ {
get get
{ {
return KLRes.InvalidCompositeKey + Environment.NewLine return KLRes.InvalidCompositeKey + Environment.NewLine +
+ Environment.NewLine + KLRes.InvalidCompositeKeyHint; KLRes.InvalidCompositeKeyHint;
} }
} }

View File

@@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2014 Dominik Reichl <dominik.reichl@t-online.de> Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by

View File

@@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2014 Dominik Reichl <dominik.reichl@t-online.de> Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -30,6 +30,7 @@ using System.Security.Cryptography;
using ModernKeePassLib.Security; using ModernKeePassLib.Security;
using ModernKeePassLib.Utility; using ModernKeePassLib.Utility;
using Windows.Security.Cryptography.Core; using Windows.Security.Cryptography.Core;
using ModernKeePassLib.Cryptography;
namespace ModernKeePassLib.Keys namespace ModernKeePassLib.Keys
{ {
@@ -60,18 +61,8 @@ namespace ModernKeePassLib.Keys
if(bPerformHash) if(bPerformHash)
{ {
#if ModernKeePassLib byte[] pbRaw = CryptoUtil.HashSha256(pbKeyData);
/*var sha256 = WinRTCrypto.HashAlgorithmProvider.OpenAlgorithm(HashAlgorithm.Sha256); m_pbKey = new ProtectedBinary(true, pbRaw);
var pbRaw = sha256.HashData(pbKeyData);*/
var sha256 = HashAlgorithmProvider.OpenAlgorithm(HashAlgorithmNames.Sha256);
var buffer = sha256.HashData(CryptographicBuffer.CreateFromByteArray(pbKeyData));
byte[] pbRaw;
CryptographicBuffer.CopyToByteArray(buffer, out pbRaw);
#else
SHA256Managed sha256 = new SHA256Managed();
byte[] pbRaw = sha256.ComputeHash(pbKeyData);
#endif
m_pbKey = new ProtectedBinary(true, pbRaw);
} }
else m_pbKey = new ProtectedBinary(true, pbKeyData); else m_pbKey = new ProtectedBinary(true, pbKeyData);
} }

View File

@@ -138,18 +138,7 @@ namespace ModernKeePassLib.Keys
else if(iLength == 64) pbKey = LoadHexKey32(pbFileData); else if(iLength == 64) pbKey = LoadHexKey32(pbFileData);
if(pbKey == null) if(pbKey == null)
{ pbKey = CryptoUtil.HashSha256(pbFileData);
#if ModernKeePassLib
/*var sha256 = WinRTCrypto.HashAlgorithmProvider.OpenAlgorithm(HashAlgorithm.Sha256);
pbKey = sha256.HashData(pbFileData);*/
var sha256 = HashAlgorithmProvider.OpenAlgorithm(HashAlgorithmNames.Sha256);
var buffer = sha256.HashData(CryptographicBuffer.CreateFromByteArray(pbFileData));
CryptographicBuffer.CopyToByteArray(buffer, out pbKey);
#else
SHA256Managed sha256 = new SHA256Managed();
pbKey = sha256.ComputeHash(pbFileData);
#endif
}
return pbKey; return pbKey;
} }
@@ -169,12 +158,15 @@ namespace ModernKeePassLib.Keys
try try
{ {
string strHex = StrUtil.Utf8.GetString(pbFileData, 0, 64); if(!StrUtil.IsHexString(pbFileData, true)) return null;
if(!StrUtil.IsHexString(strHex, true)) return null;
string strHex = StrUtil.Utf8.GetString(pbFileData, 0, pbFileData.Length);
byte[] pbKey = MemUtil.HexStringToByteArray(strHex); byte[] pbKey = MemUtil.HexStringToByteArray(strHex);
if((pbKey == null) || (pbKey.Length != 32)) if((pbKey == null) || (pbKey.Length != 32))
{
Debug.Assert(false);
return null; return null;
}
return pbKey; return pbKey;
} }
@@ -202,21 +194,13 @@ namespace ModernKeePassLib.Keys
pbFinalKey32 = pbKey32; pbFinalKey32 = pbKey32;
else else
{ {
MemoryStream ms = new MemoryStream(); using(MemoryStream ms = new MemoryStream())
ms.Write(pbAdditionalEntropy, 0, pbAdditionalEntropy.Length); {
ms.Write(pbKey32, 0, 32); MemUtil.Write(ms, pbAdditionalEntropy);
MemUtil.Write(ms, pbKey32);
#if ModernKeePassLib pbFinalKey32 = CryptoUtil.HashSha256(ms.ToArray());
/*var sha256 = WinRTCrypto.HashAlgorithmProvider.OpenAlgorithm(HashAlgorithm.Sha256); }
pbFinalKey32 = sha256.HashData(ms.ToArray());*/
var sha256 = HashAlgorithmProvider.OpenAlgorithm(HashAlgorithmNames.Sha256);
var buffer = sha256.HashData(CryptographicBuffer.CreateFromByteArray(ms.ToArray()));
CryptographicBuffer.CopyToByteArray(buffer, out pbFinalKey32);
#else
SHA256Managed sha256 = new SHA256Managed();
pbFinalKey32 = sha256.ComputeHash(ms.ToArray());
#endif
ms.Dispose();
} }
CreateXmlKeyFile(strFilePath, pbFinalKey32); CreateXmlKeyFile(strFilePath, pbFinalKey32);
@@ -318,11 +302,14 @@ namespace ModernKeePassLib.Keys
if(pbKeyData == null) throw new ArgumentNullException("pbKeyData"); if(pbKeyData == null) throw new ArgumentNullException("pbKeyData");
IOConnectionInfo ioc = IOConnectionInfo.FromPath(strFile); IOConnectionInfo ioc = IOConnectionInfo.FromPath(strFile);
var sOut = IOConnection.OpenWrite(ioc); Stream sOut = IOConnection.OpenWrite(ioc);
#if ModernKeePassLib #if ModernKeePassLib
var settings = new XmlWriterSettings() { Encoding = StrUtil.Utf8 }; XmlWriterSettings xws = new XmlWriterSettings();
var xtw = XmlWriter.Create(sOut, settings); xws.Encoding = StrUtil.Utf8;
xws.Indent = false;
XmlWriter xtw = XmlWriter.Create(sOut, xws);
#else #else
XmlTextWriter xtw = new XmlTextWriter(sOut, StrUtil.Utf8); XmlTextWriter xtw = new XmlTextWriter(sOut, StrUtil.Utf8);
#endif #endif

View File

@@ -29,6 +29,7 @@ using System.Security.Cryptography;
using ModernKeePassLib.Security; using ModernKeePassLib.Security;
using ModernKeePassLib.Utility; using ModernKeePassLib.Utility;
using Windows.Security.Cryptography.Core; using Windows.Security.Cryptography.Core;
using ModernKeePassLib.Cryptography;
namespace ModernKeePassLib.Keys namespace ModernKeePassLib.Keys
{ {
@@ -73,19 +74,13 @@ namespace ModernKeePassLib.Keys
Debug.Assert(pbPasswordUtf8 != null); Debug.Assert(pbPasswordUtf8 != null);
if(pbPasswordUtf8 == null) throw new ArgumentNullException("pbPasswordUtf8"); if(pbPasswordUtf8 == null) throw new ArgumentNullException("pbPasswordUtf8");
#if ModernKeePassLib #if (DEBUG && !KeePassLibSD)
/*var sha256 = WinRTCrypto.HashAlgorithmProvider.OpenAlgorithm(HashAlgorithm.Sha256); Debug.Assert(ValidatePassword(pbPasswordUtf8));
var pbRaw = sha256.HashData(pbPasswordUtf8);*/
var sha256 = HashAlgorithmProvider.OpenAlgorithm(HashAlgorithmNames.Sha256);
var buffer = sha256.HashData(CryptographicBuffer.CreateFromByteArray(pbPasswordUtf8));
byte[] pbRaw;
CryptographicBuffer.CopyToByteArray(buffer, out pbRaw);
#else
SHA256Managed sha256 = new SHA256Managed();
byte[] pbRaw = sha256.ComputeHash(pbPasswordUtf8);
#endif #endif
m_psPassword = new ProtectedString(true, pbPasswordUtf8); byte[] pbRaw = CryptoUtil.HashSha256(pbPasswordUtf8);
m_psPassword = new ProtectedString(true, pbPasswordUtf8);
m_pbKeyData = new ProtectedBinary(true, pbRaw); m_pbKeyData = new ProtectedBinary(true, pbRaw);
} }
@@ -94,5 +89,24 @@ namespace ModernKeePassLib.Keys
// m_psPassword = null; // m_psPassword = null;
// m_pbKeyData = null; // m_pbKeyData = null;
// } // }
}
#if (DEBUG && !KeePassLibSD)
private static bool ValidatePassword(byte[] pb)
{
try
{
string str = StrUtil.Utf8.GetString(pb, 0, pb.Length);
#if ModernKeePassLib
// TODO: find a way to implement this
return true;
#else
return str.IsNormalized(NormalizationForm.FormC);
#endif
}
catch(Exception) { Debug.Assert(false); }
return false;
}
#endif
}
} }

View File

@@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2014 Dominik Reichl <dominik.reichl@t-online.de> Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -18,11 +18,16 @@
*/ */
using System; using System;
using System.Security; using System.Diagnostics;
using System.Security.Cryptography;
using System.IO; using System.IO;
using System.Security;
#if !KeePassUAP
using Windows.Security.Cryptography;
#endif
using ModernKeePassLib.Cryptography; using ModernKeePassLib.Cryptography;
using ModernKeePassLib.Native;
using ModernKeePassLib.Resources; using ModernKeePassLib.Resources;
using ModernKeePassLib.Security; using ModernKeePassLib.Security;
using ModernKeePassLib.Utility; using ModernKeePassLib.Utility;
@@ -37,7 +42,7 @@ namespace ModernKeePassLib.Keys
private ProtectedBinary m_pbKeyData = null; private ProtectedBinary m_pbKeyData = null;
// Constant initialization vector (unique for KeePass) // Constant initialization vector (unique for KeePass)
private static readonly byte[] m_pbEntropy = new byte[]{ private static readonly byte[] m_pbEntropy = new byte[] {
0xDE, 0x13, 0x5B, 0x5F, 0x18, 0xA3, 0x46, 0x70, 0xDE, 0x13, 0x5B, 0x5F, 0x18, 0xA3, 0x46, 0x70,
0xB2, 0x57, 0x24, 0x29, 0x69, 0x88, 0x98, 0xE6 0xB2, 0x57, 0x24, 0x29, 0x69, 0x88, 0x98, 0xE6
}; };
@@ -67,10 +72,14 @@ namespace ModernKeePassLib.Keys
byte[] pbKey = LoadUserKey(false); byte[] pbKey = LoadUserKey(false);
if(pbKey == null) pbKey = CreateUserKey(); if(pbKey == null) pbKey = CreateUserKey();
if(pbKey == null) throw new SecurityException(KLRes.UserAccountKeyError); if(pbKey == null) // Should never happen
{
Debug.Assert(false);
throw new SecurityException(KLRes.UserAccountKeyError);
}
m_pbKeyData = new ProtectedBinary(true, pbKey); m_pbKeyData = new ProtectedBinary(true, pbKey);
Array.Clear(pbKey, 0, pbKey.Length); MemUtil.ZeroByteArray(pbKey);
} }
// public void Clear() // public void Clear()
@@ -80,7 +89,7 @@ namespace ModernKeePassLib.Keys
private static string GetUserKeyFilePath(bool bCreate) private static string GetUserKeyFilePath(bool bCreate)
{ {
#if KeePassRT #if ModernKeePassLib
string strUserDir = Windows.Storage.ApplicationData.Current.RoamingFolder.Path; string strUserDir = Windows.Storage.ApplicationData.Current.RoamingFolder.Path;
#else #else
string strUserDir = Environment.GetFolderPath( string strUserDir = Environment.GetFolderPath(
@@ -94,10 +103,10 @@ namespace ModernKeePassLib.Keys
Directory.CreateDirectory(strUserDir); Directory.CreateDirectory(strUserDir);
strUserDir = UrlUtil.EnsureTerminatingSeparator(strUserDir, false); strUserDir = UrlUtil.EnsureTerminatingSeparator(strUserDir, false);
return strUserDir + UserKeyFileName; return (strUserDir + UserKeyFileName);
} }
private static byte[] LoadUserKey(bool bShowWarning) private static byte[] LoadUserKey(bool bThrow)
{ {
byte[] pbKey = null; byte[] pbKey = null;
@@ -109,13 +118,10 @@ namespace ModernKeePassLib.Keys
pbKey = ProtectedData.Unprotect(pbProtectedKey, m_pbEntropy, pbKey = ProtectedData.Unprotect(pbProtectedKey, m_pbEntropy,
DataProtectionScope.CurrentUser); DataProtectionScope.CurrentUser);
Array.Clear(pbProtectedKey, 0, pbProtectedKey.Length);
} }
catch(Exception exLoad) catch(Exception)
{ {
if(bShowWarning) MessageService.ShowWarning(exLoad); if(bThrow) throw;
pbKey = null; pbKey = null;
} }
#endif #endif
@@ -125,28 +131,23 @@ namespace ModernKeePassLib.Keys
private static byte[] CreateUserKey() private static byte[] CreateUserKey()
{ {
byte[] pbKey = null; #if KeePassLibSD
return null;
#else
string strFilePath = GetUserKeyFilePath(true);
#if !KeePassLibSD byte[] pbRandomKey = CryptoRandom.Instance.GetRandomBytes(64);
try byte[] pbProtectedKey = ProtectedData.Protect(pbRandomKey,
{ m_pbEntropy, DataProtectionScope.CurrentUser);
string strFilePath = GetUserKeyFilePath(true);
byte[] pbRandomKey = CryptoRandom.Instance.GetRandomBytes(64); File.WriteAllBytes(strFilePath, pbProtectedKey);
byte[] pbProtectedKey = ProtectedData.Protect(pbRandomKey,
m_pbEntropy, DataProtectionScope.CurrentUser);
File.WriteAllBytes(strFilePath, pbProtectedKey); byte[] pbKey = LoadUserKey(true);
Debug.Assert(MemUtil.ArraysEqual(pbKey, pbRandomKey));
Array.Clear(pbProtectedKey, 0, pbProtectedKey.Length);
Array.Clear(pbRandomKey, 0, pbRandomKey.Length);
pbKey = LoadUserKey(true);
}
catch(Exception) { pbKey = null; }
#endif
MemUtil.ZeroByteArray(pbRandomKey);
return pbKey; return pbKey;
#endif
} }
} }
} }

View File

@@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2014 Dominik Reichl <dominik.reichl@t-online.de> Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by

View File

@@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2014 Dominik Reichl <dominik.reichl@t-online.de> Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by

View File

@@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2014 Dominik Reichl <dominik.reichl@t-online.de> Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by

View File

@@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2014 Dominik Reichl <dominik.reichl@t-online.de> Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by

View File

@@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2014 Dominik Reichl <dominik.reichl@t-online.de> Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by

View File

@@ -42,18 +42,32 @@
<ItemGroup> <ItemGroup>
<Compile Include="Collections\AutoTypeConfig.cs" /> <Compile Include="Collections\AutoTypeConfig.cs" />
<Compile Include="Collections\ProtectedBinaryDictionary.cs" /> <Compile Include="Collections\ProtectedBinaryDictionary.cs" />
<Compile Include="Collections\ProtectedBinarySet.cs" />
<Compile Include="Collections\ProtectedStringDictionary.cs" /> <Compile Include="Collections\ProtectedStringDictionary.cs" />
<Compile Include="Collections\PwObjectList.cs" /> <Compile Include="Collections\PwObjectList.cs" />
<Compile Include="Collections\PwObjectPool.cs" /> <Compile Include="Collections\PwObjectPool.cs" />
<Compile Include="Collections\StringDictionaryEx.cs" /> <Compile Include="Collections\StringDictionaryEx.cs" />
<Compile Include="Collections\VariantDictionary.cs" />
<Compile Include="Cryptography\Cipher\ChaCha20Cipher.cs" />
<Compile Include="Cryptography\Cipher\ChaCha20Engine.cs" />
<Compile Include="Cryptography\Cipher\CipherPool.cs" /> <Compile Include="Cryptography\Cipher\CipherPool.cs" />
<Compile Include="Cryptography\Cipher\CtrBlockCipher.cs" />
<Compile Include="Cryptography\Cipher\Salsa20Cipher.cs" /> <Compile Include="Cryptography\Cipher\Salsa20Cipher.cs" />
<Compile Include="Cryptography\Cipher\StandardAesEngine.cs" /> <Compile Include="Cryptography\Cipher\StandardAesEngine.cs" />
<Compile Include="Cryptography\CryptographicHashExtensions.cs" /> <Compile Include="Cryptography\CryptographicHashExtensions.cs" />
<Compile Include="Cryptography\CryptoRandom.cs" /> <Compile Include="Cryptography\CryptoRandom.cs" />
<Compile Include="Cryptography\CryptoRandomStream.cs" /> <Compile Include="Cryptography\CryptoRandomStream.cs" />
<Compile Include="Cryptography\Cipher\ICipherEngine.cs" /> <Compile Include="Cryptography\Cipher\ICipherEngine.cs" />
<Compile Include="Cryptography\CryptoUtil.cs" />
<Compile Include="Cryptography\Hash\Blake2b.cs" />
<Compile Include="Cryptography\HmacOtp.cs" /> <Compile Include="Cryptography\HmacOtp.cs" />
<Compile Include="Cryptography\KeyDerivation\AesKdf.cs" />
<Compile Include="Cryptography\KeyDerivation\AesKdf.GCrypt.cs" />
<Compile Include="Cryptography\KeyDerivation\Argon2Kdf.Core.cs" />
<Compile Include="Cryptography\KeyDerivation\Argon2Kdf.cs" />
<Compile Include="Cryptography\KeyDerivation\KdfEngine.cs" />
<Compile Include="Cryptography\KeyDerivation\KdfParameters.cs" />
<Compile Include="Cryptography\KeyDerivation\KdfPool.cs" />
<Compile Include="Cryptography\PasswordGenerator\CharSetBasedGenerator.cs" /> <Compile Include="Cryptography\PasswordGenerator\CharSetBasedGenerator.cs" />
<Compile Include="Cryptography\PasswordGenerator\CustomPwGenerator.cs" /> <Compile Include="Cryptography\PasswordGenerator\CustomPwGenerator.cs" />
<Compile Include="Cryptography\PasswordGenerator\CustomPwGeneratorPool.cs" /> <Compile Include="Cryptography\PasswordGenerator\CustomPwGeneratorPool.cs" />
@@ -97,7 +111,11 @@
<Compile Include="Serialization\FileLock.cs" /> <Compile Include="Serialization\FileLock.cs" />
<Compile Include="Serialization\FileTransactionEx.cs" /> <Compile Include="Serialization\FileTransactionEx.cs" />
<Compile Include="Serialization\HashedBlockStream.cs" /> <Compile Include="Serialization\HashedBlockStream.cs" />
<Compile Include="Serialization\HmacBlockStream.cs" />
<Compile Include="Serialization\IOConnection.cs" /> <Compile Include="Serialization\IOConnection.cs" />
<Compile Include="Serialization\IocProperties.cs" />
<Compile Include="Serialization\IocPropertyInfo.cs" />
<Compile Include="Serialization\IocPropertyInfoPool.cs" />
<Compile Include="Serialization\KdbxFile.cs" /> <Compile Include="Serialization\KdbxFile.cs" />
<Compile Include="Serialization\KdbxFile.Read.cs" /> <Compile Include="Serialization\KdbxFile.Read.cs" />
<Compile Include="Serialization\KdbxFile.Read.Streamed.cs" /> <Compile Include="Serialization\KdbxFile.Read.Streamed.cs" />
@@ -126,7 +144,7 @@
<None Include="Native\NativeLib.cs" /> <None Include="Native\NativeLib.cs" />
<None Include="Native\NativeMethods.cs" /> <None Include="Native\NativeMethods.cs" />
<None Include="Native\NativeMethods.Unix.cs" /> <None Include="Native\NativeMethods.Unix.cs" />
<None Include="Keys\KcpUserAccount.cs" /> <Compile Include="Keys\KcpUserAccount.cs" />
<None Include="Translation\KPControlCustomization.cs" /> <None Include="Translation\KPControlCustomization.cs" />
<None Include="Translation\KPFormCustomization.cs" /> <None Include="Translation\KPFormCustomization.cs" />
<None Include="Translation\KPStringTable.cs" /> <None Include="Translation\KPStringTable.cs" />
@@ -154,4 +172,5 @@
<HintPath>Libs\Windows.winmd</HintPath> <HintPath>Libs\Windows.winmd</HintPath>
</Reference> </Reference>
</ItemGroup> </ItemGroup>
<ItemGroup />
</Project> </Project>

View File

@@ -2,7 +2,7 @@
<package > <package >
<metadata> <metadata>
<id>ModernKeePassLib</id> <id>ModernKeePassLib</id>
<version>2.28.4000</version> <version>2.28.5000</version>
<title>ModernKeePassLib</title> <title>ModernKeePassLib</title>
<authors>Geoffroy Bonneville</authors> <authors>Geoffroy Bonneville</authors>
<owners>Geoffroy Bonneville</owners> <owners>Geoffroy Bonneville</owners>
@@ -10,9 +10,9 @@
<projectUrl>https://github.com/wismna/ModernKeePass</projectUrl> <projectUrl>https://github.com/wismna/ModernKeePass</projectUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance> <requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>Portable KeePass Password Management Library that targets .Net Standard and WinRT</description> <description>Portable KeePass Password Management Library that targets .Net Standard and WinRT</description>
<releaseNotes>Bump to 2.28, write mode activated with BouncyCastle</releaseNotes> <releaseNotes>Load image in GfxUtil made async to avoid a hang</releaseNotes>
<copyright>Copyright © 2017 Geoffroy Bonneville</copyright> <copyright>Copyright © 2017 Geoffroy Bonneville</copyright>
<tags>KeePass KeePassLib Portable PCL</tags> <tags>KeePass KeePassLib Portable PCL NetStandard</tags>
<dependencies> <dependencies>
<group targetFramework=".NETStandard1.2"> <group targetFramework=".NETStandard1.2">
<dependency id="Microsoft.NETCore.Portable.Compatibility" version="1.0.2" exclude="Build,Analyzers" /> <dependency id="Microsoft.NETCore.Portable.Compatibility" version="1.0.2" exclude="Build,Analyzers" />

View File

@@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2014 Dominik Reichl <dominik.reichl@t-online.de> Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by

View File

@@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2014 Dominik Reichl <dominik.reichl@t-online.de> Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -18,13 +18,13 @@
*/ */
using System; using System;
using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
#if ModernKeePassLib #if ModernKeePassLib
using Image = Splat.IBitmap; using Image = Splat.IBitmap;
#else #else
using System.Drawing; using System.Drawing;
#endif #endif
using System.IO;
using ModernKeePassLib.Utility; using ModernKeePassLib.Utility;
@@ -35,9 +35,15 @@ namespace ModernKeePassLib
/// </summary> /// </summary>
public sealed class PwCustomIcon public sealed class PwCustomIcon
{ {
private PwUuid m_pwUuid; private readonly PwUuid m_pwUuid;
private byte[] m_pbImageDataPng; private readonly byte[] m_pbImageDataPng;
private Image m_pCachedImage;
private readonly Image m_imgOrg;
private Dictionary<long, Image> m_dImageCache = new Dictionary<long, Image>();
// Recommended maximum sizes, not obligatory
internal const int MaxWidth = 128;
internal const int MaxHeight = 128;
public PwUuid Uuid public PwUuid Uuid
{ {
@@ -49,9 +55,14 @@ namespace ModernKeePassLib
get { return m_pbImageDataPng; } get { return m_pbImageDataPng; }
} }
[Obsolete("Use GetImage instead.")]
public Image Image public Image Image
{ {
get { return m_pCachedImage; } #if (!KeePassLibSD && !KeePassUAP)
get { return GetImage(16, 16); } // Backward compatibility
#else
get { return GetImage(); } // Backward compatibility
#endif
} }
public PwCustomIcon(PwUuid pwUuid, byte[] pbImageDataPng) public PwCustomIcon(PwUuid pwUuid, byte[] pbImageDataPng)
@@ -59,22 +70,65 @@ namespace ModernKeePassLib
Debug.Assert(pwUuid != null); Debug.Assert(pwUuid != null);
if(pwUuid == null) throw new ArgumentNullException("pwUuid"); if(pwUuid == null) throw new ArgumentNullException("pwUuid");
Debug.Assert(!pwUuid.Equals(PwUuid.Zero)); Debug.Assert(!pwUuid.Equals(PwUuid.Zero));
if(pwUuid.Equals(PwUuid.Zero)) throw new ArgumentException("pwUuid == 0"); if(pwUuid.Equals(PwUuid.Zero)) throw new ArgumentException("pwUuid == 0.");
Debug.Assert(pbImageDataPng != null); Debug.Assert(pbImageDataPng != null);
if(pbImageDataPng == null) throw new ArgumentNullException("pbImageDataPng"); if(pbImageDataPng == null) throw new ArgumentNullException("pbImageDataPng");
m_pwUuid = pwUuid; m_pwUuid = pwUuid;
m_pbImageDataPng = pbImageDataPng; m_pbImageDataPng = pbImageDataPng;
#if !KeePassLibSD
// MemoryStream ms = new MemoryStream(m_pbImageDataPng, false); // MemoryStream ms = new MemoryStream(m_pbImageDataPng, false);
// m_pCachedImage = Image.FromStream(ms); // m_imgOrg = Image.FromStream(ms);
// ms.Close(); // ms.Close();
m_pCachedImage = GfxUtil.LoadImage(m_pbImageDataPng); #if ModernKeePassLib
try { m_imgOrg = GfxUtil.LoadImage(m_pbImageDataPng).GetAwaiter().GetResult(); }
#else #else
m_pCachedImage = null; try { m_imgOrg = GfxUtil.LoadImage(m_pbImageDataPng); }
#endif #endif
catch(Exception) { Debug.Assert(false); m_imgOrg = null; }
if(m_imgOrg != null)
m_dImageCache[GetID((int)m_imgOrg.Width, (int)m_imgOrg.Height)] =
m_imgOrg;
} }
}
private static long GetID(int w, int h)
{
return (((long)w << 32) ^ (long)h);
}
/// <summary>
/// Get the icon as an <c>Image</c> (original size).
/// </summary>
public Image GetImage()
{
return m_imgOrg;
}
#if (!KeePassLibSD && !KeePassUAP)
/// <summary>
/// Get the icon as an <c>Image</c> (with the specified size).
/// </summary>
/// <param name="w">Width of the returned image.</param>
/// <param name="h">Height of the returned image.</param>
public Image GetImage(int w, int h)
{
if(w < 0) { Debug.Assert(false); return m_imgOrg; }
if(h < 0) { Debug.Assert(false); return m_imgOrg; }
if(m_imgOrg == null) return null;
long lID = GetID(w, h);
Image img;
if(m_dImageCache.TryGetValue(lID, out img)) return img;
#if ModernKeePassLib
img = GfxUtil.ScaleImage(m_pbImageDataPng, w, h).GetAwaiter().GetResult();
#else
img = GfxUtil.ScaleImage(m_imgOrg, w, h, ScaleTransformFlags.UIIcon);
#endif
m_dImageCache[lID] = img;
return img;
}
#endif
}
} }

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2014 Dominik Reichl <dominik.reichl@t-online.de> Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -19,9 +19,9 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Xml.Serialization;
using System.ComponentModel; using System.ComponentModel;
using System.Diagnostics; using System.Diagnostics;
using System.Xml.Serialization;
using ModernKeePassLib.Delegates; using ModernKeePassLib.Delegates;
using ModernKeePassLib.Interfaces; using ModernKeePassLib.Interfaces;
@@ -55,63 +55,64 @@ namespace ModernKeePassLib
/// e.g. 2.19 = 0x02130000. /// e.g. 2.19 = 0x02130000.
/// It is highly recommended to use <c>FileVersion64</c> instead. /// It is highly recommended to use <c>FileVersion64</c> instead.
/// </summary> /// </summary>
public const uint Version32 = 0x021C0000; public const uint Version32 = 0x02250000;
/// <summary> /// <summary>
/// Version, encoded as 64-bit unsigned integer /// Version, encoded as 64-bit unsigned integer
/// (component-wise, 16 bits per component). /// (component-wise, 16 bits per component).
/// </summary> /// </summary>
public const ulong FileVersion64 = 0x0002001C00000000UL; public const ulong FileVersion64 = 0x0002002500000000UL;
/// <summary> /// <summary>
/// Version, encoded as string. /// Version, encoded as string.
/// </summary> /// </summary>
public const string VersionString = "2.28"; public const string VersionString = "2.37";
public const string Copyright = @"Copyright © 2003-2014 Dominik Reichl"; public const string Copyright = @"Copyright © 2003-2017 Dominik Reichl";
/// <summary> /// <summary>
/// Product website URL. Terminated by a forward slash. /// Product website URL. Terminated by a forward slash.
/// </summary> /// </summary>
public const string HomepageUrl = "http://keepass.info/"; public const string HomepageUrl = "https://keepass.info/";
/// <summary>
/// Product donations URL.
/// </summary>
public const string DonationsUrl = "http://keepass.info/donate.html";
/// <summary>
/// URL to the online plugins page.
/// </summary>
public const string PluginsUrl = "http://keepass.info/plugins.html";
/// <summary> /// <summary>
/// URL to the online translations page. /// URL to the online translations page.
/// </summary> /// </summary>
public const string TranslationsUrl = "http://keepass.info/translations.html"; public const string TranslationsUrl = "https://keepass.info/translations.html";
/// <summary> /// <summary>
/// URL to a TXT file (eventually compressed) that contains information /// URL to the online plugins page.
/// about the latest KeePass version available on the website.
/// </summary> /// </summary>
public const string VersionUrl = "http://keepass.info/update/version2x.txt.gz"; public const string PluginsUrl = "https://keepass.info/plugins.html";
/// <summary>
/// Product donations URL.
/// </summary>
public const string DonationsUrl = "https://keepass.info/donate.html";
/// <summary> /// <summary>
/// URL to the root path of the online KeePass help. Terminated by /// URL to the root path of the online KeePass help. Terminated by
/// a forward slash. /// a forward slash.
/// </summary> /// </summary>
public const string HelpUrl = "http://keepass.info/help/"; public const string HelpUrl = "https://keepass.info/help/";
/// <summary>
/// URL to a TXT file (eventually compressed) that contains information
/// about the latest KeePass version available on the website.
/// </summary>
public const string VersionUrl = "https://www.dominik-reichl.de/update/version2x.txt.gz";
/// <summary> /// <summary>
/// A <c>DateTime</c> object that represents the time when the assembly /// A <c>DateTime</c> object that represents the time when the assembly
/// was loaded. /// was loaded.
/// </summary> /// </summary>
public static readonly DateTime DtDefaultNow = DateTime.Now; public static readonly DateTime DtDefaultNow = DateTime.UtcNow;
/// <summary> /// <summary>
/// Default number of master key encryption/transformation rounds (making dictionary attacks harder). /// Default number of master key encryption/transformation rounds
/// (making dictionary attacks harder).
/// </summary> /// </summary>
public const ulong DefaultKeyEncryptionRounds = 6000; public const ulong DefaultKeyEncryptionRounds = 60000;
/// <summary> /// <summary>
/// Default identifier string for the title field. Should not contain /// Default identifier string for the title field. Should not contain
@@ -294,6 +295,22 @@ namespace ModernKeePassLib
set { m_bSearchInOther = value; } set { m_bSearchInOther = value; }
} }
private bool m_bSearchInStringNames = false;
[DefaultValue(false)]
public bool SearchInStringNames
{
get { return m_bSearchInStringNames; }
set { m_bSearchInStringNames = value; }
}
private bool m_bSearchInTags = true;
[DefaultValue(true)]
public bool SearchInTags
{
get { return m_bSearchInTags; }
set { m_bSearchInTags = value; }
}
private bool m_bSearchInUuids = false; private bool m_bSearchInUuids = false;
[DefaultValue(false)] [DefaultValue(false)]
public bool SearchInUuids public bool SearchInUuids
@@ -310,15 +327,7 @@ namespace ModernKeePassLib
set { m_bSearchInGroupNames = value; } set { m_bSearchInGroupNames = value; }
} }
private bool m_bSearchInTags = true; #if ModernKeePassLib || KeePassUAP
[DefaultValue(true)]
public bool SearchInTags
{
get { return m_bSearchInTags; }
set { m_bSearchInTags = value; }
}
#if ModernKeePassLib || KeePassRT
private StringComparison m_scType = StringComparison.OrdinalIgnoreCase; private StringComparison m_scType = StringComparison.OrdinalIgnoreCase;
#else #else
private StringComparison m_scType = StringComparison.InvariantCultureIgnoreCase; private StringComparison m_scType = StringComparison.InvariantCultureIgnoreCase;
@@ -379,20 +388,21 @@ namespace ModernKeePassLib
{ {
SearchParameters sp = new SearchParameters(); SearchParameters sp = new SearchParameters();
// sp.m_strText = string.Empty; Debug.Assert(sp.m_strText.Length == 0);
// sp.m_bRegex = false; Debug.Assert(!sp.m_bRegex);
sp.m_bSearchInTitles = false; sp.m_bSearchInTitles = false;
sp.m_bSearchInUserNames = false; sp.m_bSearchInUserNames = false;
// sp.m_bSearchInPasswords = false; Debug.Assert(!sp.m_bSearchInPasswords);
sp.m_bSearchInUrls = false; sp.m_bSearchInUrls = false;
sp.m_bSearchInNotes = false; sp.m_bSearchInNotes = false;
sp.m_bSearchInOther = false; sp.m_bSearchInOther = false;
// sp.m_bSearchInUuids = false; Debug.Assert(!sp.m_bSearchInStringNames);
// sp.SearchInGroupNames = false;
sp.m_bSearchInTags = false; sp.m_bSearchInTags = false;
// sp.m_scType = StringComparison.InvariantCultureIgnoreCase; Debug.Assert(!sp.m_bSearchInUuids);
// sp.m_bExcludeExpired = false; Debug.Assert(!sp.m_bSearchInGroupNames);
// m_bRespectEntrySearchingDisabled = true; // Debug.Assert(sp.m_scType == StringComparison.InvariantCultureIgnoreCase);
Debug.Assert(!sp.m_bExcludeExpired);
Debug.Assert(sp.m_bRespectEntrySearchingDisabled);
return sp; return sp;
} }

View File

@@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2014 Dominik Reichl <dominik.reichl@t-online.de> Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by

View File

@@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2014 Dominik Reichl <dominik.reichl@t-online.de> Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -63,6 +63,8 @@ namespace ModernKeePassLib
private List<string> m_vTags = new List<string>(); private List<string> m_vTags = new List<string>();
private StringDictionaryEx m_dCustomData = new StringDictionaryEx();
/// <summary> /// <summary>
/// UUID of this entry. /// UUID of this entry.
/// </summary> /// </summary>
@@ -272,6 +274,23 @@ namespace ModernKeePassLib
} }
} }
/// <summary>
/// Custom data container that can be used by plugins to store
/// own data in KeePass entries.
/// The data is stored in the encrypted part of encrypted
/// database files.
/// Use unique names for your items, e.g. "PluginName_ItemName".
/// </summary>
public StringDictionaryEx CustomData
{
get { return m_dCustomData; }
internal set
{
if(value == null) { Debug.Assert(false); throw new ArgumentNullException("value"); }
m_dCustomData = value;
}
}
public static EventHandler<ObjectTouchedEventArgs> EntryTouched; public static EventHandler<ObjectTouchedEventArgs> EntryTouched;
public EventHandler<ObjectTouchedEventArgs> Touched; public EventHandler<ObjectTouchedEventArgs> Touched;
@@ -290,8 +309,11 @@ namespace ModernKeePassLib
if(bSetTimes) if(bSetTimes)
{ {
m_tCreation = m_tLastMod = m_tLastAccess = DateTime dtNow = DateTime.UtcNow;
m_tParentGroupLastMod = DateTime.Now; m_tCreation = dtNow;
m_tLastMod = dtNow;
m_tLastAccess = dtNow;
m_tParentGroupLastMod = dtNow;
} }
} }
@@ -315,11 +337,22 @@ namespace ModernKeePassLib
if(bSetTimes) if(bSetTimes)
{ {
m_tCreation = m_tLastMod = m_tLastAccess = DateTime dtNow = DateTime.UtcNow;
m_tParentGroupLastMod = DateTime.Now; m_tCreation = dtNow;
m_tLastMod = dtNow;
m_tLastAccess = dtNow;
m_tParentGroupLastMod = dtNow;
} }
} }
#if DEBUG
// For display in debugger
public override string ToString()
{
return (@"PwEntry '" + m_listStrings.ReadSafe(PwDefs.TitleField) + @"'");
}
#endif
/// <summary> /// <summary>
/// Clone the current entry. The returned entry is an exact value copy /// Clone the current entry. The returned entry is an exact value copy
/// of the current entry (including UUID and parent group reference). /// of the current entry (including UUID and parent group reference).
@@ -356,6 +389,8 @@ namespace ModernKeePassLib
peNew.m_vTags = new List<string>(m_vTags); peNew.m_vTags = new List<string>(m_vTags);
peNew.m_dCustomData = m_dCustomData.CloneDeep();
return peNew; return peNew;
} }
@@ -476,6 +511,8 @@ namespace ModernKeePassLib
if(m_vTags[iTag] != pe.m_vTags[iTag]) return false; if(m_vTags[iTag] != pe.m_vTags[iTag]) return false;
} }
if(!m_dCustomData.Equals(pe.m_dCustomData)) return false;
return true; return true;
} }
@@ -492,10 +529,10 @@ namespace ModernKeePassLib
public void AssignProperties(PwEntry peTemplate, bool bOnlyIfNewer, public void AssignProperties(PwEntry peTemplate, bool bOnlyIfNewer,
bool bIncludeHistory, bool bAssignLocationChanged) bool bIncludeHistory, bool bAssignLocationChanged)
{ {
Debug.Assert(peTemplate != null); if(peTemplate == null) throw new ArgumentNullException("peTemplate"); if(peTemplate == null) { Debug.Assert(false); throw new ArgumentNullException("peTemplate"); }
if(bOnlyIfNewer && (TimeUtil.Compare(peTemplate.m_tLastMod, m_tLastMod, if(bOnlyIfNewer && (TimeUtil.Compare(peTemplate.m_tLastMod,
true) < 0)) m_tLastMod, true) < 0))
return; return;
// Template UUID should be the same as the current one // Template UUID should be the same as the current one
@@ -505,10 +542,11 @@ namespace ModernKeePassLib
if(bAssignLocationChanged) if(bAssignLocationChanged)
m_tParentGroupLastMod = peTemplate.m_tParentGroupLastMod; m_tParentGroupLastMod = peTemplate.m_tParentGroupLastMod;
m_listStrings = peTemplate.m_listStrings; m_listStrings = peTemplate.m_listStrings.CloneDeep();
m_listBinaries = peTemplate.m_listBinaries; m_listBinaries = peTemplate.m_listBinaries.CloneDeep();
m_listAutoType = peTemplate.m_listAutoType; m_listAutoType = peTemplate.m_listAutoType.CloneDeep();
if(bIncludeHistory) m_listHistory = peTemplate.m_listHistory; if(bIncludeHistory)
m_listHistory = peTemplate.m_listHistory.CloneDeep();
m_pwIcon = peTemplate.m_pwIcon; m_pwIcon = peTemplate.m_pwIcon;
m_pwCustomIconID = peTemplate.m_pwCustomIconID; // Immutable m_pwCustomIconID = peTemplate.m_pwCustomIconID; // Immutable
@@ -526,6 +564,8 @@ namespace ModernKeePassLib
m_strOverrideUrl = peTemplate.m_strOverrideUrl; m_strOverrideUrl = peTemplate.m_strOverrideUrl;
m_vTags = new List<string>(peTemplate.m_vTags); m_vTags = new List<string>(peTemplate.m_vTags);
m_dCustomData = peTemplate.m_dCustomData.CloneDeep();
} }
/// <summary> /// <summary>
@@ -549,7 +589,7 @@ namespace ModernKeePassLib
/// get touched, too.</param> /// get touched, too.</param>
public void Touch(bool bModified, bool bTouchParents) public void Touch(bool bModified, bool bTouchParents)
{ {
m_tLastAccess = DateTime.Now; m_tLastAccess = DateTime.UtcNow;
++m_uUsageCount; ++m_uUsageCount;
if(bModified) m_tLastMod = m_tLastAccess; if(bModified) m_tLastMod = m_tLastAccess;
@@ -688,7 +728,7 @@ namespace ModernKeePassLib
private void RemoveOldestBackup() private void RemoveOldestBackup()
{ {
DateTime dtMin = DateTime.MaxValue; DateTime dtMin = TimeUtil.SafeMaxValueUtc;
uint idxRemove = uint.MaxValue; uint idxRemove = uint.MaxValue;
for(uint u = 0; u < m_listHistory.UCount; ++u) for(uint u = 0; u < m_listHistory.UCount; ++u)
@@ -777,6 +817,9 @@ namespace ModernKeePassLib
foreach(string strTag in m_vTags) foreach(string strTag in m_vTags)
uSize += (ulong)strTag.Length; uSize += (ulong)strTag.Length;
foreach(KeyValuePair<string, string> kvp in m_dCustomData)
uSize += (ulong)kvp.Key.Length + (ulong)kvp.Value.Length;
return uSize; return uSize;
} }
@@ -849,7 +892,7 @@ namespace ModernKeePassLib
public void SetCreatedNow() public void SetCreatedNow()
{ {
DateTime dt = DateTime.Now; DateTime dt = DateTime.UtcNow;
m_tCreation = dt; m_tCreation = dt;
m_tLastAccess = dt; m_tLastAccess = dt;

View File

@@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2014 Dominik Reichl <dominik.reichl@t-online.de> Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -163,6 +163,26 @@ namespace ModernKeePassLib
Manual = 2 Manual = 2
} }
public enum ProxyAuthType
{
None = 0,
/// <summary>
/// Use default user credentials (provided by the system).
/// </summary>
Default = 1,
Manual = 2,
/// <summary>
/// <c>Default</c> or <c>Manual</c>, depending on whether
/// manual credentials are available.
/// This type exists for supporting upgrading from KeePass
/// 2.28 to 2.29; the user cannot select this type.
/// </summary>
Auto = 3
}
/// <summary> /// <summary>
/// Comparison modes for in-memory protected objects. /// Comparison modes for in-memory protected objects.
/// </summary> /// </summary>
@@ -257,14 +277,43 @@ namespace ModernKeePassLib
GetStdOutput = 1, GetStdOutput = 1,
WaitForExit = 2, WaitForExit = 2,
// This flag prevents any handles being garbage-collected
// before the started process has terminated, without
// blocking the current thread;
// https://sourceforge.net/p/keepass/patches/84/ // https://sourceforge.net/p/keepass/patches/84/
/// <summary>
/// This flag prevents any handles being garbage-collected
/// before the started process has terminated, without
/// blocking the current thread.
/// </summary>
GCKeepAlive = 4, GCKeepAlive = 4,
// https://sourceforge.net/p/keepass/patches/85/ // https://sourceforge.net/p/keepass/patches/85/
DoEvents = 8, DoEvents = 8,
DisableForms = 16 DisableForms = 16
} }
[Flags]
public enum ScaleTransformFlags
{
None = 0,
/// <summary>
/// <c>UIIcon</c> indicates that the returned image is going
/// to be displayed as icon in the UI and that it is not
/// subject to future changes in size.
/// </summary>
UIIcon = 1
}
public enum DesktopType
{
None = 0,
Windows,
Gnome,
Kde,
Unity,
Lxde,
Xfce,
Mate,
Cinnamon,
Pantheon
}
} }

View File

@@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2014 Dominik Reichl <dominik.reichl@t-online.de> Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -67,6 +67,8 @@ namespace ModernKeePassLib
private PwUuid m_pwLastTopVisibleEntry = PwUuid.Zero; private PwUuid m_pwLastTopVisibleEntry = PwUuid.Zero;
private StringDictionaryEx m_dCustomData = new StringDictionaryEx();
/// <summary> /// <summary>
/// UUID of this group. /// UUID of this group.
/// </summary> /// </summary>
@@ -281,6 +283,23 @@ namespace ModernKeePassLib
} }
} }
/// <summary>
/// Custom data container that can be used by plugins to store
/// own data in KeePass groups.
/// The data is stored in the encrypted part of encrypted
/// database files.
/// Use unique names for your items, e.g. "PluginName_ItemName".
/// </summary>
public StringDictionaryEx CustomData
{
get { return m_dCustomData; }
internal set
{
if(value == null) { Debug.Assert(false); throw new ArgumentNullException("value"); }
m_dCustomData = value;
}
}
public static EventHandler<ObjectTouchedEventArgs> GroupTouched; public static EventHandler<ObjectTouchedEventArgs> GroupTouched;
public EventHandler<ObjectTouchedEventArgs> Touched; public EventHandler<ObjectTouchedEventArgs> Touched;
@@ -302,8 +321,11 @@ namespace ModernKeePassLib
if(bSetTimes) if(bSetTimes)
{ {
m_tCreation = m_tLastMod = m_tLastAccess = DateTime dtNow = DateTime.UtcNow;
m_tParentGroupLastMod = DateTime.Now; m_tCreation = dtNow;
m_tLastMod = dtNow;
m_tLastAccess = dtNow;
m_tParentGroupLastMod = dtNow;
} }
} }
@@ -320,8 +342,11 @@ namespace ModernKeePassLib
if(bSetTimes) if(bSetTimes)
{ {
m_tCreation = m_tLastMod = m_tLastAccess = DateTime dtNow = DateTime.UtcNow;
m_tParentGroupLastMod = DateTime.Now; m_tCreation = dtNow;
m_tLastMod = dtNow;
m_tLastAccess = dtNow;
m_tParentGroupLastMod = dtNow;
} }
if(strName != null) m_strName = strName; if(strName != null) m_strName = strName;
@@ -329,6 +354,14 @@ namespace ModernKeePassLib
m_pwIcon = pwIcon; m_pwIcon = pwIcon;
} }
#if DEBUG
// For display in debugger
public override string ToString()
{
return (@"PwGroup '" + m_strName + @"'");
}
#endif
/// <summary> /// <summary>
/// Deeply clone the current group. The returned group will be an exact /// Deeply clone the current group. The returned group will be an exact
/// value copy of the current object (including UUID, etc.). /// value copy of the current object (including UUID, etc.).
@@ -368,6 +401,8 @@ namespace ModernKeePassLib
pg.m_pwLastTopVisibleEntry = m_pwLastTopVisibleEntry; pg.m_pwLastTopVisibleEntry = m_pwLastTopVisibleEntry;
pg.m_dCustomData = m_dCustomData.CloneDeep();
return pg; return pg;
} }
@@ -436,6 +471,8 @@ namespace ModernKeePassLib
if(!m_pwLastTopVisibleEntry.Equals(pg.m_pwLastTopVisibleEntry)) return false; if(!m_pwLastTopVisibleEntry.Equals(pg.m_pwLastTopVisibleEntry)) return false;
if(!m_dCustomData.Equals(pg.m_dCustomData)) return false;
if((pwOpt & PwCompareOptions.PropertiesOnly) == PwCompareOptions.None) if((pwOpt & PwCompareOptions.PropertiesOnly) == PwCompareOptions.None)
{ {
if(m_listEntries.UCount != pg.m_listEntries.UCount) return false; if(m_listEntries.UCount != pg.m_listEntries.UCount) return false;
@@ -501,6 +538,8 @@ namespace ModernKeePassLib
m_bEnableSearching = pgTemplate.m_bEnableSearching; m_bEnableSearching = pgTemplate.m_bEnableSearching;
m_pwLastTopVisibleEntry = pgTemplate.m_pwLastTopVisibleEntry; m_pwLastTopVisibleEntry = pgTemplate.m_pwLastTopVisibleEntry;
m_dCustomData = pgTemplate.m_dCustomData.CloneDeep();
} }
/// <summary> /// <summary>
@@ -524,7 +563,7 @@ namespace ModernKeePassLib
/// get touched, too.</param> /// get touched, too.</param>
public void Touch(bool bModified, bool bTouchParents) public void Touch(bool bModified, bool bTouchParents)
{ {
m_tLastAccess = DateTime.Now; m_tLastAccess = DateTime.UtcNow;
++m_uUsageCount; ++m_uUsageCount;
if(bModified) m_tLastMod = m_tLastAccess; if(bModified) m_tLastMod = m_tLastAccess;
@@ -629,21 +668,15 @@ namespace ModernKeePassLib
} }
} }
if(groupHandler != null) foreach(PwGroup pg in m_listGroups)
{ {
foreach(PwGroup pg in m_listGroups) if(groupHandler != null)
{ {
if(!groupHandler(pg)) return false; if(!groupHandler(pg)) return false;
}
pg.PreOrderTraverseTree(groupHandler, entryHandler); if(!pg.PreOrderTraverseTree(groupHandler, entryHandler))
} return false;
}
else // groupHandler == null
{
foreach(PwGroup pg in m_listGroups)
{
pg.PreOrderTraverseTree(null, entryHandler);
}
} }
return true; return true;
@@ -733,96 +766,112 @@ namespace ModernKeePassLib
/// <summary> /// <summary>
/// Search this group and all subgroups for entries. /// Search this group and all subgroups for entries.
/// </summary> /// </summary>
/// <param name="sp">Specifies the search method.</param> /// <param name="sp">Specifies the search parameters.</param>
/// <param name="listStorage">Entry list in which the search results will /// <param name="lResults">Entry list in which the search results
/// be stored.</param> /// will be stored.</param>
public void SearchEntries(SearchParameters sp, PwObjectList<PwEntry> listStorage) public void SearchEntries(SearchParameters sp, PwObjectList<PwEntry> lResults)
{ {
SearchEntries(sp, listStorage, null); SearchEntries(sp, lResults, null);
} }
/// <summary> /// <summary>
/// Search this group and all subgroups for entries. /// Search this group and all subgroups for entries.
/// </summary> /// </summary>
/// <param name="sp">Specifies the search method.</param> /// <param name="sp">Specifies the search parameters.</param>
/// <param name="listStorage">Entry list in which the search results will /// <param name="lResults">Entry list in which the search results
/// be stored.</param> /// will be stored.</param>
/// <param name="slStatus">Optional status reporting object.</param> /// <param name="slStatus">Optional status reporting object.</param>
public void SearchEntries(SearchParameters sp, PwObjectList<PwEntry> listStorage, public void SearchEntries(SearchParameters sp, PwObjectList<PwEntry> lResults,
IStatusLogger slStatus) IStatusLogger slStatus)
{ {
if(sp == null) { Debug.Assert(false); return; } if(sp == null) { Debug.Assert(false); return; }
if(listStorage == null) { Debug.Assert(false); return; } if(lResults == null) { Debug.Assert(false); return; }
ulong uCurEntries = 0, uTotalEntries = 0; PwObjectList<PwEntry> lCand = GetEntries(true);
DateTime dtNow = DateTime.UtcNow;
List<string> lTerms = StrUtil.SplitSearchTerms(sp.SearchString); PwObjectList<PwEntry> l = new PwObjectList<PwEntry>();
if((lTerms.Count <= 1) || sp.RegularExpression) foreach(PwEntry pe in lCand)
{ {
if(slStatus != null) uTotalEntries = GetEntriesCount(true); if(sp.RespectEntrySearchingDisabled && !pe.GetSearchingEnabled())
SearchEntriesSingle(sp, listStorage, slStatus, ref uCurEntries, continue;
uTotalEntries); if(sp.ExcludeExpired && pe.Expires && (pe.ExpiryTime <= dtNow))
return; continue;
l.Add(pe);
} }
lCand = l;
List<string> lTerms;
if(sp.RegularExpression)
{
lTerms = new List<string>();
lTerms.Add((sp.SearchString ?? string.Empty).Trim());
}
else lTerms = StrUtil.SplitSearchTerms(sp.SearchString);
// Search longer strings first (for improved performance) // Search longer strings first (for improved performance)
lTerms.Sort(StrUtil.CompareLengthGt); lTerms.Sort(StrUtil.CompareLengthGt);
string strFullSearch = sp.SearchString; // Backup ulong uPrcEntries = 0, uTotalEntries = lCand.UCount;
SearchParameters spSub = sp.Clone();
PwGroup pg = this;
for(int iTerm = 0; iTerm < lTerms.Count; ++iTerm) for(int iTerm = 0; iTerm < lTerms.Count; ++iTerm)
{ {
// Update counters for a better state guess // Update counters for a better state guess
if(slStatus != null) if(slStatus != null)
{ {
ulong uRemRounds = (ulong)(lTerms.Count - iTerm); ulong uRemRounds = (ulong)(lTerms.Count - iTerm);
uTotalEntries = uCurEntries + (uRemRounds * uTotalEntries = uPrcEntries + (uRemRounds *
pg.GetEntriesCount(true)); lCand.UCount);
} }
PwGroup pgNew = new PwGroup(); spSub.SearchString = lTerms[iTerm]; // No trim
// spSub.RespectEntrySearchingDisabled = false; // Ignored by sub
sp.SearchString = lTerms[iTerm]; // spSub.ExcludeExpired = false; // Ignored by sub
bool bNegate = false; bool bNegate = false;
if(sp.SearchString.StartsWith("-")) if(spSub.SearchString.StartsWith(@"-") &&
(spSub.SearchString.Length >= 2))
{ {
sp.SearchString = sp.SearchString.Substring(1); spSub.SearchString = spSub.SearchString.Substring(1);
bNegate = (sp.SearchString.Length > 0); bNegate = true;
} }
if(!pg.SearchEntriesSingle(sp, pgNew.Entries, slStatus, l = new PwObjectList<PwEntry>();
ref uCurEntries, uTotalEntries)) if(!SearchEntriesSingle(lCand, spSub, l, slStatus,
ref uPrcEntries, uTotalEntries))
{ {
pg = null; lCand.Clear();
break; break;
} }
if(bNegate) if(bNegate)
{ {
PwObjectList<PwEntry> lCand = pg.GetEntries(true); PwObjectList<PwEntry> lRem = new PwObjectList<PwEntry>();
foreach(PwEntry pe in lCand)
pg = new PwGroup();
foreach(PwEntry peCand in lCand)
{ {
if(pgNew.Entries.IndexOf(peCand) < 0) pg.Entries.Add(peCand); if(l.IndexOf(pe) < 0) lRem.Add(pe);
} }
lCand = lRem;
} }
else pg = pgNew; else lCand = l;
} }
if(pg != null) listStorage.Add(pg.Entries); Debug.Assert(lResults.UCount == 0);
sp.SearchString = strFullSearch; // Restore lResults.Clear();
lResults.Add(lCand);
} }
private bool SearchEntriesSingle(SearchParameters spIn, private static bool SearchEntriesSingle(PwObjectList<PwEntry> lSource,
PwObjectList<PwEntry> listStorage, IStatusLogger slStatus, SearchParameters sp, PwObjectList<PwEntry> lResults,
ref ulong uCurEntries, ulong uTotalEntries) IStatusLogger slStatus, ref ulong uPrcEntries, ulong uTotalEntries)
{ {
SearchParameters sp = spIn.Clone(); if(lSource == null) { Debug.Assert(false); return true; }
if(sp.SearchString == null) { Debug.Assert(false); return true; } if(sp == null) { Debug.Assert(false); return true; }
sp.SearchString = sp.SearchString.Trim(); if(lResults == null) { Debug.Assert(false); return true; }
Debug.Assert(lResults.UCount == 0);
bool bTitle = sp.SearchInTitles; bool bTitle = sp.SearchInTitles;
bool bUserName = sp.SearchInUserNames; bool bUserName = sp.SearchInUserNames;
@@ -830,22 +879,17 @@ namespace ModernKeePassLib
bool bUrl = sp.SearchInUrls; bool bUrl = sp.SearchInUrls;
bool bNotes = sp.SearchInNotes; bool bNotes = sp.SearchInNotes;
bool bOther = sp.SearchInOther; bool bOther = sp.SearchInOther;
bool bStringName = sp.SearchInStringNames;
bool bTags = sp.SearchInTags;
bool bUuids = sp.SearchInUuids; bool bUuids = sp.SearchInUuids;
bool bGroupName = sp.SearchInGroupNames; bool bGroupName = sp.SearchInGroupNames;
bool bTags = sp.SearchInTags; // bool bExcludeExpired = sp.ExcludeExpired;
bool bExcludeExpired = sp.ExcludeExpired; // bool bRespectEntrySearchingDisabled = sp.RespectEntrySearchingDisabled;
bool bRespectEntrySearchingDisabled = sp.RespectEntrySearchingDisabled;
DateTime dtNow = DateTime.Now;
Regex rx = null; Regex rx = null;
if(sp.RegularExpression) if(sp.RegularExpression)
{ {
#if ModernKeePassLib || KeePassRT RegexOptions ro = RegexOptions.None; // RegexOptions.Compiled
RegexOptions ro = RegexOptions.None;
#else
RegexOptions ro = RegexOptions.Compiled;
#endif
if((sp.ComparisonMode == StringComparison.CurrentCultureIgnoreCase) || if((sp.ComparisonMode == StringComparison.CurrentCultureIgnoreCase) ||
#if !ModernKeePassLib && !KeePassRT #if !ModernKeePassLib && !KeePassRT
(sp.ComparisonMode == StringComparison.InvariantCultureIgnoreCase) || (sp.ComparisonMode == StringComparison.InvariantCultureIgnoreCase) ||
@@ -858,46 +902,26 @@ namespace ModernKeePassLib
rx = new Regex(sp.SearchString, ro); rx = new Regex(sp.SearchString, ro);
} }
ulong uLocalCurEntries = uCurEntries; ulong uLocalPrcEntries = uPrcEntries;
EntryHandler eh = null; if(sp.SearchString.Length == 0) lResults.Add(lSource);
if(sp.SearchString.Length <= 0) // Report all
{
eh = delegate(PwEntry pe)
{
if(slStatus != null)
{
if(!slStatus.SetProgress((uint)((uLocalCurEntries *
100UL) / uTotalEntries))) return false;
++uLocalCurEntries;
}
if(bRespectEntrySearchingDisabled && !pe.GetSearchingEnabled())
return true; // Skip
if(bExcludeExpired && pe.Expires && (dtNow > pe.ExpiryTime))
return true; // Skip
listStorage.Add(pe);
return true;
};
}
else else
{ {
eh = delegate(PwEntry pe) foreach(PwEntry pe in lSource)
{ {
if(slStatus != null) if(slStatus != null)
{ {
if(!slStatus.SetProgress((uint)((uLocalCurEntries * if(!slStatus.SetProgress((uint)((uLocalPrcEntries *
100UL) / uTotalEntries))) return false; 100UL) / uTotalEntries))) return false;
++uLocalCurEntries; ++uLocalPrcEntries;
} }
if(bRespectEntrySearchingDisabled && !pe.GetSearchingEnabled()) // if(bRespectEntrySearchingDisabled && !pe.GetSearchingEnabled())
return true; // Skip // continue;
if(bExcludeExpired && pe.Expires && (dtNow > pe.ExpiryTime)) // if(bExcludeExpired && pe.Expires && (pe.ExpiryTime <= dtNow))
return true; // Skip // continue;
uint uInitialResults = listStorage.UCount; uint uInitialResults = lResults.UCount;
foreach(KeyValuePair<string, ProtectedString> kvp in pe.Strings) foreach(KeyValuePair<string, ProtectedString> kvp in pe.Strings)
{ {
@@ -906,76 +930,87 @@ namespace ModernKeePassLib
if(strKey == PwDefs.TitleField) if(strKey == PwDefs.TitleField)
{ {
if(bTitle) SearchEvalAdd(sp, kvp.Value.ReadString(), if(bTitle) SearchEvalAdd(sp, kvp.Value.ReadString(),
rx, pe, listStorage); rx, pe, lResults);
} }
else if(strKey == PwDefs.UserNameField) else if(strKey == PwDefs.UserNameField)
{ {
if(bUserName) SearchEvalAdd(sp, kvp.Value.ReadString(), if(bUserName) SearchEvalAdd(sp, kvp.Value.ReadString(),
rx, pe, listStorage); rx, pe, lResults);
} }
else if(strKey == PwDefs.PasswordField) else if(strKey == PwDefs.PasswordField)
{ {
if(bPassword) SearchEvalAdd(sp, kvp.Value.ReadString(), if(bPassword) SearchEvalAdd(sp, kvp.Value.ReadString(),
rx, pe, listStorage); rx, pe, lResults);
} }
else if(strKey == PwDefs.UrlField) else if(strKey == PwDefs.UrlField)
{ {
if(bUrl) SearchEvalAdd(sp, kvp.Value.ReadString(), if(bUrl) SearchEvalAdd(sp, kvp.Value.ReadString(),
rx, pe, listStorage); rx, pe, lResults);
} }
else if(strKey == PwDefs.NotesField) else if(strKey == PwDefs.NotesField)
{ {
if(bNotes) SearchEvalAdd(sp, kvp.Value.ReadString(), if(bNotes) SearchEvalAdd(sp, kvp.Value.ReadString(),
rx, pe, listStorage); rx, pe, lResults);
} }
else if(bOther) else if(bOther)
SearchEvalAdd(sp, kvp.Value.ReadString(), SearchEvalAdd(sp, kvp.Value.ReadString(),
rx, pe, listStorage); rx, pe, lResults);
// An entry can match only once => break if we have added it // An entry can match only once => break if we have added it
if(listStorage.UCount > uInitialResults) break; if(lResults.UCount != uInitialResults) break;
} }
if(bUuids && (listStorage.UCount == uInitialResults)) if(bStringName)
SearchEvalAdd(sp, pe.Uuid.ToHexString(), rx, pe, listStorage); {
foreach(KeyValuePair<string, ProtectedString> kvp in pe.Strings)
{
if(lResults.UCount != uInitialResults) break;
if(bGroupName && (listStorage.UCount == uInitialResults) && SearchEvalAdd(sp, kvp.Key, rx, pe, lResults);
(pe.ParentGroup != null)) }
SearchEvalAdd(sp, pe.ParentGroup.Name, rx, pe, listStorage); }
if(bTags) if(bTags)
{ {
foreach(string strTag in pe.Tags) foreach(string strTag in pe.Tags)
{ {
if(listStorage.UCount != uInitialResults) break; // Match if(lResults.UCount != uInitialResults) break;
SearchEvalAdd(sp, strTag, rx, pe, listStorage); SearchEvalAdd(sp, strTag, rx, pe, lResults);
} }
} }
return true; if(bUuids && (lResults.UCount == uInitialResults))
}; SearchEvalAdd(sp, pe.Uuid.ToHexString(), rx, pe, lResults);
if(bGroupName && (lResults.UCount == uInitialResults) &&
(pe.ParentGroup != null))
SearchEvalAdd(sp, pe.ParentGroup.Name, rx, pe, lResults);
}
} }
if(!PreOrderTraverseTree(null, eh)) return false; uPrcEntries = uLocalPrcEntries;
uCurEntries = uLocalCurEntries;
return true; return true;
} }
private static void SearchEvalAdd(SearchParameters sp, string strDataField, private static void SearchEvalAdd(SearchParameters sp, string strData,
Regex rx, PwEntry pe, PwObjectList<PwEntry> lResults) Regex rx, PwEntry pe, PwObjectList<PwEntry> lResults)
{ {
bool bMatch = false; if(sp == null) { Debug.Assert(false); return; }
if(strData == null) { Debug.Assert(false); return; }
if(pe == null) { Debug.Assert(false); return; }
if(lResults == null) { Debug.Assert(false); return; }
bool bMatch;
if(rx == null) if(rx == null)
bMatch = (strDataField.IndexOf(sp.SearchString, bMatch = (strData.IndexOf(sp.SearchString,
sp.ComparisonMode) >= 0); sp.ComparisonMode) >= 0);
else bMatch = rx.IsMatch(strDataField); else bMatch = rx.IsMatch(strData);
if(!bMatch && (sp.DataTransformationFn != null)) if(!bMatch && (sp.DataTransformationFn != null))
{ {
string strCmp = sp.DataTransformationFn(strDataField, pe); string strCmp = sp.DataTransformationFn(strData, pe);
if(!object.ReferenceEquals(strCmp, strDataField)) if(!object.ReferenceEquals(strCmp, strData))
{ {
if(rx == null) if(rx == null)
bMatch = (strCmp.IndexOf(sp.SearchString, bMatch = (strCmp.IndexOf(sp.SearchString,
@@ -1395,15 +1430,20 @@ namespace ModernKeePassLib
public PwObjectList<PwEntry> GetEntries(bool bIncludeSubGroupEntries) public PwObjectList<PwEntry> GetEntries(bool bIncludeSubGroupEntries)
{ {
if(bIncludeSubGroupEntries == false) return m_listEntries; PwObjectList<PwEntry> l = new PwObjectList<PwEntry>();
PwObjectList<PwEntry> list = m_listEntries.CloneShallow(); GroupHandler gh = delegate(PwGroup pg)
foreach(PwGroup pgSub in m_listGroups)
{ {
list.Add(pgSub.GetEntries(true)); l.Add(pg.Entries);
} return true;
};
return list; gh(this);
if(bIncludeSubGroupEntries)
PreOrderTraverseTree(gh, null);
Debug.Assert(l.UCount == GetEntriesCount(bIncludeSubGroupEntries));
return l;
} }
/// <summary> /// <summary>
@@ -1476,7 +1516,7 @@ namespace ModernKeePassLib
if(bTakeOwnership) subGroup.m_pParentGroup = this; if(bTakeOwnership) subGroup.m_pParentGroup = this;
if(bUpdateLocationChangedOfSub) subGroup.LocationChanged = DateTime.Now; if(bUpdateLocationChangedOfSub) subGroup.LocationChanged = DateTime.UtcNow;
} }
/// <summary> /// <summary>
@@ -1511,7 +1551,7 @@ namespace ModernKeePassLib
// only assign it to the new one // only assign it to the new one
if(bTakeOwnership) pe.ParentGroup = this; if(bTakeOwnership) pe.ParentGroup = this;
if(bUpdateLocationChangedOfEntry) pe.LocationChanged = DateTime.Now; if(bUpdateLocationChangedOfEntry) pe.LocationChanged = DateTime.UtcNow;
} }
public void SortSubGroups(bool bRecursive) public void SortSubGroups(bool bRecursive)
@@ -1527,7 +1567,7 @@ namespace ModernKeePassLib
public void DeleteAllObjects(PwDatabase pdContext) public void DeleteAllObjects(PwDatabase pdContext)
{ {
DateTime dtNow = DateTime.Now; DateTime dtNow = DateTime.UtcNow;
foreach(PwEntry pe in m_listEntries) foreach(PwEntry pe in m_listEntries)
{ {
@@ -1571,7 +1611,7 @@ namespace ModernKeePassLib
public void SetCreatedNow(bool bRecursive) public void SetCreatedNow(bool bRecursive)
{ {
DateTime dt = DateTime.Now; DateTime dt = DateTime.UtcNow;
m_tCreation = dt; m_tCreation = dt;
m_tLastAccess = dt; m_tLastAccess = dt;

View File

@@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2014 Dominik Reichl <dominik.reichl@t-online.de> Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by

View File

@@ -27,14 +27,18 @@ namespace ModernKeePassLib.Resources
if(dictNew == null) throw new ArgumentNullException("dictNew"); if(dictNew == null) throw new ArgumentNullException("dictNew");
m_strCryptoStreamFailed = TryGetEx(dictNew, "CryptoStreamFailed", m_strCryptoStreamFailed); m_strCryptoStreamFailed = TryGetEx(dictNew, "CryptoStreamFailed", m_strCryptoStreamFailed);
m_strEncAlgorithmAes = TryGetEx(dictNew, "EncAlgorithmAes", m_strEncAlgorithmAes); m_strEncDataTooLarge = TryGetEx(dictNew, "EncDataTooLarge", m_strEncDataTooLarge);
m_strErrorInClipboard = TryGetEx(dictNew, "ErrorInClipboard", m_strErrorInClipboard); m_strErrorInClipboard = TryGetEx(dictNew, "ErrorInClipboard", m_strErrorInClipboard);
m_strExpect100Continue = TryGetEx(dictNew, "Expect100Continue", m_strExpect100Continue);
m_strFatalError = TryGetEx(dictNew, "FatalError", m_strFatalError); m_strFatalError = TryGetEx(dictNew, "FatalError", m_strFatalError);
m_strFatalErrorText = TryGetEx(dictNew, "FatalErrorText", m_strFatalErrorText); m_strFatalErrorText = TryGetEx(dictNew, "FatalErrorText", m_strFatalErrorText);
m_strFileCorrupted = TryGetEx(dictNew, "FileCorrupted", m_strFileCorrupted); m_strFileCorrupted = TryGetEx(dictNew, "FileCorrupted", m_strFileCorrupted);
m_strFileHeaderEndEarly = TryGetEx(dictNew, "FileHeaderEndEarly", m_strFileHeaderEndEarly); m_strFileHeaderCorrupted = TryGetEx(dictNew, "FileHeaderCorrupted", m_strFileHeaderCorrupted);
m_strFileIncomplete = TryGetEx(dictNew, "FileIncomplete", m_strFileIncomplete);
m_strFileIncompleteExpc = TryGetEx(dictNew, "FileIncompleteExpc", m_strFileIncompleteExpc);
m_strFileLoadFailed = TryGetEx(dictNew, "FileLoadFailed", m_strFileLoadFailed); m_strFileLoadFailed = TryGetEx(dictNew, "FileLoadFailed", m_strFileLoadFailed);
m_strFileLockedWrite = TryGetEx(dictNew, "FileLockedWrite", m_strFileLockedWrite); m_strFileLockedWrite = TryGetEx(dictNew, "FileLockedWrite", m_strFileLockedWrite);
m_strFileNewVerOrPlgReq = TryGetEx(dictNew, "FileNewVerOrPlgReq", m_strFileNewVerOrPlgReq);
m_strFileNewVerReq = TryGetEx(dictNew, "FileNewVerReq", m_strFileNewVerReq); m_strFileNewVerReq = TryGetEx(dictNew, "FileNewVerReq", m_strFileNewVerReq);
m_strFileSaveCorruptionWarning = TryGetEx(dictNew, "FileSaveCorruptionWarning", m_strFileSaveCorruptionWarning); m_strFileSaveCorruptionWarning = TryGetEx(dictNew, "FileSaveCorruptionWarning", m_strFileSaveCorruptionWarning);
m_strFileSaveFailed = TryGetEx(dictNew, "FileSaveFailed", m_strFileSaveFailed); m_strFileSaveFailed = TryGetEx(dictNew, "FileSaveFailed", m_strFileSaveFailed);
@@ -44,28 +48,39 @@ namespace ModernKeePassLib.Resources
m_strFileVersionUnsupported = TryGetEx(dictNew, "FileVersionUnsupported", m_strFileVersionUnsupported); m_strFileVersionUnsupported = TryGetEx(dictNew, "FileVersionUnsupported", m_strFileVersionUnsupported);
m_strFinalKeyCreationFailed = TryGetEx(dictNew, "FinalKeyCreationFailed", m_strFinalKeyCreationFailed); m_strFinalKeyCreationFailed = TryGetEx(dictNew, "FinalKeyCreationFailed", m_strFinalKeyCreationFailed);
m_strFrameworkNotImplExcp = TryGetEx(dictNew, "FrameworkNotImplExcp", m_strFrameworkNotImplExcp); m_strFrameworkNotImplExcp = TryGetEx(dictNew, "FrameworkNotImplExcp", m_strFrameworkNotImplExcp);
m_strGeneral = TryGetEx(dictNew, "General", m_strGeneral);
m_strInvalidCompositeKey = TryGetEx(dictNew, "InvalidCompositeKey", m_strInvalidCompositeKey); m_strInvalidCompositeKey = TryGetEx(dictNew, "InvalidCompositeKey", m_strInvalidCompositeKey);
m_strInvalidCompositeKeyHint = TryGetEx(dictNew, "InvalidCompositeKeyHint", m_strInvalidCompositeKeyHint); m_strInvalidCompositeKeyHint = TryGetEx(dictNew, "InvalidCompositeKeyHint", m_strInvalidCompositeKeyHint);
m_strInvalidDataWhileDecoding = TryGetEx(dictNew, "InvalidDataWhileDecoding", m_strInvalidDataWhileDecoding); m_strInvalidDataWhileDecoding = TryGetEx(dictNew, "InvalidDataWhileDecoding", m_strInvalidDataWhileDecoding);
m_strKeePass1xHint = TryGetEx(dictNew, "KeePass1xHint", m_strKeePass1xHint); m_strKeePass1xHint = TryGetEx(dictNew, "KeePass1xHint", m_strKeePass1xHint);
m_strKeyBits = TryGetEx(dictNew, "KeyBits", m_strKeyBits);
m_strKeyFileDbSel = TryGetEx(dictNew, "KeyFileDbSel", m_strKeyFileDbSel); m_strKeyFileDbSel = TryGetEx(dictNew, "KeyFileDbSel", m_strKeyFileDbSel);
m_strMasterSeedLengthInvalid = TryGetEx(dictNew, "MasterSeedLengthInvalid", m_strMasterSeedLengthInvalid); m_strMasterSeedLengthInvalid = TryGetEx(dictNew, "MasterSeedLengthInvalid", m_strMasterSeedLengthInvalid);
m_strOldFormat = TryGetEx(dictNew, "OldFormat", m_strOldFormat); m_strOldFormat = TryGetEx(dictNew, "OldFormat", m_strOldFormat);
m_strPassive = TryGetEx(dictNew, "Passive", m_strPassive);
m_strPreAuth = TryGetEx(dictNew, "PreAuth", m_strPreAuth);
m_strTimeout = TryGetEx(dictNew, "Timeout", m_strTimeout);
m_strTryAgainSecs = TryGetEx(dictNew, "TryAgainSecs", m_strTryAgainSecs); m_strTryAgainSecs = TryGetEx(dictNew, "TryAgainSecs", m_strTryAgainSecs);
m_strUnknownHeaderId = TryGetEx(dictNew, "UnknownHeaderId", m_strUnknownHeaderId); m_strUnknownHeaderId = TryGetEx(dictNew, "UnknownHeaderId", m_strUnknownHeaderId);
m_strUnknownKdf = TryGetEx(dictNew, "UnknownKdf", m_strUnknownKdf);
m_strUserAccountKeyError = TryGetEx(dictNew, "UserAccountKeyError", m_strUserAccountKeyError); m_strUserAccountKeyError = TryGetEx(dictNew, "UserAccountKeyError", m_strUserAccountKeyError);
m_strUserAgent = TryGetEx(dictNew, "UserAgent", m_strUserAgent);
} }
private static readonly string[] m_vKeyNames = { private static readonly string[] m_vKeyNames = {
"CryptoStreamFailed", "CryptoStreamFailed",
"EncAlgorithmAes", "EncDataTooLarge",
"ErrorInClipboard", "ErrorInClipboard",
"Expect100Continue",
"FatalError", "FatalError",
"FatalErrorText", "FatalErrorText",
"FileCorrupted", "FileCorrupted",
"FileHeaderEndEarly", "FileHeaderCorrupted",
"FileIncomplete",
"FileIncompleteExpc",
"FileLoadFailed", "FileLoadFailed",
"FileLockedWrite", "FileLockedWrite",
"FileNewVerOrPlgReq",
"FileNewVerReq", "FileNewVerReq",
"FileSaveCorruptionWarning", "FileSaveCorruptionWarning",
"FileSaveFailed", "FileSaveFailed",
@@ -75,16 +90,23 @@ namespace ModernKeePassLib.Resources
"FileVersionUnsupported", "FileVersionUnsupported",
"FinalKeyCreationFailed", "FinalKeyCreationFailed",
"FrameworkNotImplExcp", "FrameworkNotImplExcp",
"General",
"InvalidCompositeKey", "InvalidCompositeKey",
"InvalidCompositeKeyHint", "InvalidCompositeKeyHint",
"InvalidDataWhileDecoding", "InvalidDataWhileDecoding",
"KeePass1xHint", "KeePass1xHint",
"KeyBits",
"KeyFileDbSel", "KeyFileDbSel",
"MasterSeedLengthInvalid", "MasterSeedLengthInvalid",
"OldFormat", "OldFormat",
"Passive",
"PreAuth",
"Timeout",
"TryAgainSecs", "TryAgainSecs",
"UnknownHeaderId", "UnknownHeaderId",
"UserAccountKeyError" "UnknownKdf",
"UserAccountKeyError",
"UserAgent"
}; };
public static string[] GetKeyNames() public static string[] GetKeyNames()
@@ -103,15 +125,15 @@ namespace ModernKeePassLib.Resources
get { return m_strCryptoStreamFailed; } get { return m_strCryptoStreamFailed; }
} }
private static string m_strEncAlgorithmAes = private static string m_strEncDataTooLarge =
@"AES/Rijndael (256-Bit Key)"; @"The data is too large to be encrypted/decrypted securely using {PARAM}.";
/// <summary> /// <summary>
/// Look up a localized string similar to /// Look up a localized string similar to
/// 'AES/Rijndael (256-Bit Key)'. /// 'The data is too large to be encrypted/decrypted securely using {PARAM}.'.
/// </summary> /// </summary>
public static string EncAlgorithmAes public static string EncDataTooLarge
{ {
get { return m_strEncAlgorithmAes; } get { return m_strEncDataTooLarge; }
} }
private static string m_strErrorInClipboard = private static string m_strErrorInClipboard =
@@ -125,6 +147,17 @@ namespace ModernKeePassLib.Resources
get { return m_strErrorInClipboard; } get { return m_strErrorInClipboard; }
} }
private static string m_strExpect100Continue =
@"Expect 100-Continue responses";
/// <summary>
/// Look up a localized string similar to
/// 'Expect 100-Continue responses'.
/// </summary>
public static string Expect100Continue
{
get { return m_strExpect100Continue; }
}
private static string m_strFatalError = private static string m_strFatalError =
@"Fatal Error"; @"Fatal Error";
/// <summary> /// <summary>
@@ -158,15 +191,37 @@ namespace ModernKeePassLib.Resources
get { return m_strFileCorrupted; } get { return m_strFileCorrupted; }
} }
private static string m_strFileHeaderEndEarly = private static string m_strFileHeaderCorrupted =
@"The file header is corrupted! Some header data was declared but is not present."; @"The file header is corrupted.";
/// <summary> /// <summary>
/// Look up a localized string similar to /// Look up a localized string similar to
/// 'The file header is corrupted! Some header data was declared but is not present.'. /// 'The file header is corrupted.'.
/// </summary> /// </summary>
public static string FileHeaderEndEarly public static string FileHeaderCorrupted
{ {
get { return m_strFileHeaderEndEarly; } get { return m_strFileHeaderCorrupted; }
}
private static string m_strFileIncomplete =
@"Data is missing at the end of the file, i.e. the file is incomplete.";
/// <summary>
/// Look up a localized string similar to
/// 'Data is missing at the end of the file, i.e. the file is incomplete.'.
/// </summary>
public static string FileIncomplete
{
get { return m_strFileIncomplete; }
}
private static string m_strFileIncompleteExpc =
@"Less data than expected could be read from the file.";
/// <summary>
/// Look up a localized string similar to
/// 'Less data than expected could be read from the file.'.
/// </summary>
public static string FileIncompleteExpc
{
get { return m_strFileIncompleteExpc; }
} }
private static string m_strFileLoadFailed = private static string m_strFileLoadFailed =
@@ -191,6 +246,17 @@ namespace ModernKeePassLib.Resources
get { return m_strFileLockedWrite; } get { return m_strFileLockedWrite; }
} }
private static string m_strFileNewVerOrPlgReq =
@"A newer KeePass version or a plugin is required to open this file.";
/// <summary>
/// Look up a localized string similar to
/// 'A newer KeePass version or a plugin is required to open this file.'.
/// </summary>
public static string FileNewVerOrPlgReq
{
get { return m_strFileNewVerOrPlgReq; }
}
private static string m_strFileNewVerReq = private static string m_strFileNewVerReq =
@"A newer KeePass version is required to open this file."; @"A newer KeePass version is required to open this file.";
/// <summary> /// <summary>
@@ -290,6 +356,17 @@ namespace ModernKeePassLib.Resources
get { return m_strFrameworkNotImplExcp; } get { return m_strFrameworkNotImplExcp; }
} }
private static string m_strGeneral =
@"General";
/// <summary>
/// Look up a localized string similar to
/// 'General'.
/// </summary>
public static string General
{
get { return m_strGeneral; }
}
private static string m_strInvalidCompositeKey = private static string m_strInvalidCompositeKey =
@"The composite key is invalid!"; @"The composite key is invalid!";
/// <summary> /// <summary>
@@ -334,6 +411,17 @@ namespace ModernKeePassLib.Resources
get { return m_strKeePass1xHint; } get { return m_strKeePass1xHint; }
} }
private static string m_strKeyBits =
@"{PARAM}-bit key";
/// <summary>
/// Look up a localized string similar to
/// '{PARAM}-bit key'.
/// </summary>
public static string KeyBits
{
get { return m_strKeyBits; }
}
private static string m_strKeyFileDbSel = private static string m_strKeyFileDbSel =
@"Database files cannot be used as key files."; @"Database files cannot be used as key files.";
/// <summary> /// <summary>
@@ -367,6 +455,39 @@ namespace ModernKeePassLib.Resources
get { return m_strOldFormat; } get { return m_strOldFormat; }
} }
private static string m_strPassive =
@"Passive";
/// <summary>
/// Look up a localized string similar to
/// 'Passive'.
/// </summary>
public static string Passive
{
get { return m_strPassive; }
}
private static string m_strPreAuth =
@"Pre-authenticate";
/// <summary>
/// Look up a localized string similar to
/// 'Pre-authenticate'.
/// </summary>
public static string PreAuth
{
get { return m_strPreAuth; }
}
private static string m_strTimeout =
@"Timeout";
/// <summary>
/// Look up a localized string similar to
/// 'Timeout'.
/// </summary>
public static string Timeout
{
get { return m_strTimeout; }
}
private static string m_strTryAgainSecs = private static string m_strTryAgainSecs =
@"Please try it again in a few seconds."; @"Please try it again in a few seconds.";
/// <summary> /// <summary>
@@ -389,6 +510,17 @@ namespace ModernKeePassLib.Resources
get { return m_strUnknownHeaderId; } get { return m_strUnknownHeaderId; }
} }
private static string m_strUnknownKdf =
@"Unknown key derivation function!";
/// <summary>
/// Look up a localized string similar to
/// 'Unknown key derivation function!'.
/// </summary>
public static string UnknownKdf
{
get { return m_strUnknownKdf; }
}
private static string m_strUserAccountKeyError = private static string m_strUserAccountKeyError =
@"The operating system did not grant KeePass read/write access to the user profile folder, where the protected user key is stored."; @"The operating system did not grant KeePass read/write access to the user profile folder, where the protected user key is stored.";
/// <summary> /// <summary>
@@ -399,5 +531,16 @@ namespace ModernKeePassLib.Resources
{ {
get { return m_strUserAccountKeyError; } get { return m_strUserAccountKeyError; }
} }
private static string m_strUserAgent =
@"User agent";
/// <summary>
/// Look up a localized string similar to
/// 'User agent'.
/// </summary>
public static string UserAgent
{
get { return m_strUserAgent; }
}
} }
} }

View File

@@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2014 Dominik Reichl <dominik.reichl@t-online.de> Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -18,8 +18,8 @@
*/ */
using System; using System;
using System.Threading;
using System.Diagnostics; using System.Diagnostics;
using System.Threading;
using ModernKeePassLib.Cryptography; using ModernKeePassLib.Cryptography;
using ModernKeePassLib.Cryptography.Cipher; using ModernKeePassLib.Cryptography.Cipher;
@@ -72,13 +72,13 @@ namespace ModernKeePassLib.Security
{ {
None = 0, None = 0,
ProtectedMemory, ProtectedMemory,
Salsa20, ChaCha20,
ExtCrypt ExtCrypt
} }
// ProtectedMemory is supported only on Windows 2000 SP3 and higher // ProtectedMemory is supported only on Windows 2000 SP3 and higher
#if !KeePassLibSD #if !KeePassLibSD
private static bool? g_bProtectedMemorySupported = null; private static bool? g_obProtectedMemorySupported = null;
#endif #endif
private static bool ProtectedMemorySupported private static bool ProtectedMemorySupported
{ {
@@ -87,14 +87,14 @@ namespace ModernKeePassLib.Security
#if KeePassLibSD #if KeePassLibSD
return false; return false;
#else #else
bool? ob = g_bProtectedMemorySupported; bool? ob = g_obProtectedMemorySupported;
if(ob.HasValue) return ob.Value; if(ob.HasValue) return ob.Value;
// Mono does not implement any encryption for ProtectedMemory; // Mono does not implement any encryption for ProtectedMemory;
// https://sourceforge.net/p/keepass/feature-requests/1907/ // https://sourceforge.net/p/keepass/feature-requests/1907/
if(NativeLib.IsUnix()) if(NativeLib.IsUnix())
{ {
g_bProtectedMemorySupported = false; g_obProtectedMemorySupported = false;
return false; return false;
} }
@@ -115,7 +115,7 @@ namespace ModernKeePassLib.Security
} }
catch(Exception) { } // Windows 98 / ME catch(Exception) { } // Windows 98 / ME
g_bProtectedMemorySupported = ob; g_obProtectedMemorySupported = ob;
return ob.Value; return ob.Value;
#endif #endif
} }
@@ -135,7 +135,7 @@ namespace ModernKeePassLib.Security
private PbMemProt m_mp = PbMemProt.None; // Actual protection private PbMemProt m_mp = PbMemProt.None; // Actual protection
private object m_objSync = new object(); private readonly object m_objSync = new object();
private static byte[] g_pbKey32 = null; private static byte[] g_pbKey32 = null;
@@ -162,7 +162,7 @@ namespace ModernKeePassLib.Security
/// </summary> /// </summary>
public ProtectedBinary() public ProtectedBinary()
{ {
Init(false, new byte[0]); Init(false, MemUtil.EmptyByteArray, 0, 0);
} }
/// <summary> /// <summary>
@@ -177,7 +177,27 @@ namespace ModernKeePassLib.Security
/// i.e. the caller is responsible for clearing it.</param> /// i.e. the caller is responsible for clearing it.</param>
public ProtectedBinary(bool bEnableProtection, byte[] pbData) public ProtectedBinary(bool bEnableProtection, byte[] pbData)
{ {
Init(bEnableProtection, pbData); if(pbData == null) throw new ArgumentNullException("pbData");
Init(bEnableProtection, pbData, 0, pbData.Length);
}
/// <summary>
/// Construct a new protected binary data object.
/// </summary>
/// <param name="bEnableProtection">If this paremeter is <c>true</c>,
/// the data will be encrypted in memory. If it is <c>false</c>, the
/// data is stored in plain-text in the process memory.</param>
/// <param name="pbData">Value of the protected object.
/// The input parameter is not modified and
/// <c>ProtectedBinary</c> doesn't take ownership of the data,
/// i.e. the caller is responsible for clearing it.</param>
/// <param name="iOffset">Offset for <paramref name="pbData" />.</param>
/// <param name="cbSize">Size for <paramref name="pbData" />.</param>
public ProtectedBinary(bool bEnableProtection, byte[] pbData,
int iOffset, int cbSize)
{
Init(bEnableProtection, pbData, iOffset, cbSize);
} }
/// <summary> /// <summary>
@@ -193,13 +213,19 @@ namespace ModernKeePassLib.Security
if(xbProtected == null) throw new ArgumentNullException("xbProtected"); if(xbProtected == null) throw new ArgumentNullException("xbProtected");
byte[] pb = xbProtected.ReadPlainText(); byte[] pb = xbProtected.ReadPlainText();
Init(bEnableProtection, pb); Init(bEnableProtection, pb, 0, pb.Length);
MemUtil.ZeroByteArray(pb);
if(bEnableProtection) MemUtil.ZeroByteArray(pb);
} }
private void Init(bool bEnableProtection, byte[] pbData) private void Init(bool bEnableProtection, byte[] pbData, int iOffset,
int cbSize)
{ {
if(pbData == null) throw new ArgumentNullException("pbData"); if(pbData == null) throw new ArgumentNullException("pbData");
if(iOffset < 0) throw new ArgumentOutOfRangeException("iOffset");
if(cbSize < 0) throw new ArgumentOutOfRangeException("cbSize");
if(iOffset > (pbData.Length - cbSize))
throw new ArgumentOutOfRangeException("cbSize");
#if KeePassLibSD #if KeePassLibSD
m_lID = ++g_lCurID; m_lID = ++g_lCurID;
@@ -208,15 +234,15 @@ namespace ModernKeePassLib.Security
#endif #endif
m_bProtected = bEnableProtection; m_bProtected = bEnableProtection;
m_uDataLen = (uint)pbData.Length; m_uDataLen = (uint)cbSize;
const int bs = ProtectedBinary.BlockSize; const int bs = ProtectedBinary.BlockSize;
int nBlocks = (int)m_uDataLen / bs; int nBlocks = cbSize / bs;
if((nBlocks * bs) < (int)m_uDataLen) ++nBlocks; if((nBlocks * bs) < cbSize) ++nBlocks;
Debug.Assert((nBlocks * bs) >= (int)m_uDataLen); Debug.Assert((nBlocks * bs) >= cbSize);
m_pbData = new byte[nBlocks * bs]; m_pbData = new byte[nBlocks * bs];
Array.Copy(pbData, m_pbData, (int)m_uDataLen); Array.Copy(pbData, iOffset, m_pbData, 0, cbSize);
Encrypt(); Encrypt();
} }
@@ -258,11 +284,13 @@ namespace ModernKeePassLib.Security
if(pbUpd != null) pbKey32 = pbUpd; if(pbUpd != null) pbKey32 = pbUpd;
} }
Salsa20Cipher s = new Salsa20Cipher(pbKey32, byte[] pbIV = new byte[12];
BitConverter.GetBytes(m_lID)); MemUtil.UInt64ToBytesEx((ulong)m_lID, pbIV, 4);
s.Encrypt(m_pbData, m_pbData.Length, true); using(ChaCha20Cipher c = new ChaCha20Cipher(pbKey32, pbIV, true))
s.Dispose(); {
m_mp = PbMemProt.Salsa20; c.Encrypt(m_pbData, 0, m_pbData.Length);
}
m_mp = PbMemProt.ChaCha20;
} }
private void Decrypt() private void Decrypt()
@@ -271,12 +299,14 @@ namespace ModernKeePassLib.Security
if(m_mp == PbMemProt.ProtectedMemory) if(m_mp == PbMemProt.ProtectedMemory)
ProtectedMemory.Unprotect(m_pbData, MemoryProtectionScope.SameProcess); ProtectedMemory.Unprotect(m_pbData, MemoryProtectionScope.SameProcess);
else if(m_mp == PbMemProt.Salsa20) else if(m_mp == PbMemProt.ChaCha20)
{ {
Salsa20Cipher s = new Salsa20Cipher(g_pbKey32, byte[] pbIV = new byte[12];
BitConverter.GetBytes(m_lID)); MemUtil.UInt64ToBytesEx((ulong)m_lID, pbIV, 4);
s.Encrypt(m_pbData, m_pbData.Length, true); using(ChaCha20Cipher c = new ChaCha20Cipher(g_pbKey32, pbIV, true))
s.Dispose(); {
c.Decrypt(m_pbData, 0, m_pbData.Length);
}
} }
else if(m_mp == PbMemProt.ExtCrypt) else if(m_mp == PbMemProt.ExtCrypt)
m_fExtCrypt(m_pbData, PbCryptFlags.Decrypt, m_lID); m_fExtCrypt(m_pbData, PbCryptFlags.Decrypt, m_lID);
@@ -295,7 +325,7 @@ namespace ModernKeePassLib.Security
/// protected data and can therefore be cleared safely.</returns> /// protected data and can therefore be cleared safely.</returns>
public byte[] ReadData() public byte[] ReadData()
{ {
if(m_uDataLen == 0) return new byte[0]; if(m_uDataLen == 0) return MemUtil.EmptyByteArray;
byte[] pbReturn = new byte[m_uDataLen]; byte[] pbReturn = new byte[m_uDataLen];

View File

@@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2014 Dominik Reichl <dominik.reichl@t-online.de> Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -18,8 +18,8 @@
*/ */
using System; using System;
using System.Text;
using System.Diagnostics; using System.Diagnostics;
using System.Text;
using ModernKeePassLib.Cryptography; using ModernKeePassLib.Cryptography;
using ModernKeePassLib.Utility; using ModernKeePassLib.Utility;
@@ -150,7 +150,8 @@ namespace ModernKeePassLib.Security
byte[] pb = xbProtected.ReadPlainText(); byte[] pb = xbProtected.ReadPlainText();
Init(bEnableProtection, pb); Init(bEnableProtection, pb);
MemUtil.ZeroByteArray(pb);
if(bEnableProtection) MemUtil.ZeroByteArray(pb);
} }
private void Init(bool bEnableProtection, string str) private void Init(bool bEnableProtection, string str)
@@ -242,7 +243,8 @@ namespace ModernKeePassLib.Security
byte[] pb = ReadUtf8(); byte[] pb = ReadUtf8();
ProtectedString ps = new ProtectedString(bProtect, pb); ProtectedString ps = new ProtectedString(bProtect, pb);
MemUtil.ZeroByteArray(pb);
if(bProtect) MemUtil.ZeroByteArray(pb);
return ps; return ps;
} }
@@ -280,7 +282,7 @@ namespace ModernKeePassLib.Security
} }
finally finally
{ {
Array.Clear(v, 0, v.Length); MemUtil.ZeroArray<char>(v);
MemUtil.ZeroByteArray(pb); MemUtil.ZeroByteArray(pb);
} }
@@ -290,7 +292,7 @@ namespace ModernKeePassLib.Security
Debug.Assert(utf8.GetString(pbNew, 0, pbNew.Length) == Debug.Assert(utf8.GetString(pbNew, 0, pbNew.Length) ==
ReadString().Insert(iStart, strInsert)); ReadString().Insert(iStart, strInsert));
Array.Clear(vNew, 0, vNew.Length); MemUtil.ZeroArray<char>(vNew);
MemUtil.ZeroByteArray(pbNew); MemUtil.ZeroByteArray(pbNew);
return ps; return ps;
} }
@@ -326,7 +328,7 @@ namespace ModernKeePassLib.Security
} }
finally finally
{ {
Array.Clear(v, 0, v.Length); MemUtil.ZeroArray<char>(v);
MemUtil.ZeroByteArray(pb); MemUtil.ZeroByteArray(pb);
} }
@@ -336,7 +338,7 @@ namespace ModernKeePassLib.Security
Debug.Assert(utf8.GetString(pbNew, 0, pbNew.Length) == Debug.Assert(utf8.GetString(pbNew, 0, pbNew.Length) ==
ReadString().Remove(iStart, nCount)); ReadString().Remove(iStart, nCount));
Array.Clear(vNew, 0, vNew.Length); MemUtil.ZeroArray<char>(vNew);
MemUtil.ZeroByteArray(pbNew); MemUtil.ZeroByteArray(pbNew);
return ps; return ps;
} }

View File

@@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2014 Dominik Reichl <dominik.reichl@t-online.de> Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by

View File

@@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2014 Dominik Reichl <dominik.reichl@t-online.de> Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -19,8 +19,8 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text;
using System.IO; using System.IO;
using System.Text;
using ModernKeePassLib.Utility; using ModernKeePassLib.Utility;
@@ -31,7 +31,7 @@ namespace ModernKeePassLib.Serialization
private Stream m_s; private Stream m_s;
// private Encoding m_enc; // See constructor // private Encoding m_enc; // See constructor
private string m_strReadExcp; private string m_strReadExcp; // May be null
public string ReadExceptionText public string ReadExceptionText
{ {
get { return m_strReadExcp; } get { return m_strReadExcp; }
@@ -53,8 +53,7 @@ namespace ModernKeePassLib.Serialization
public BinaryReaderEx(Stream input, Encoding encoding, public BinaryReaderEx(Stream input, Encoding encoding,
string strReadExceptionText) string strReadExceptionText)
{ {
if(input == null) if(input == null) throw new ArgumentNullException("input");
throw new ArgumentNullException("input");
m_s = input; m_s = input;
// m_enc = encoding; // Not used yet // m_enc = encoding; // Not used yet
@@ -68,20 +67,18 @@ namespace ModernKeePassLib.Serialization
byte[] pb = MemUtil.Read(m_s, nCount); byte[] pb = MemUtil.Read(m_s, nCount);
if((pb == null) || (pb.Length != nCount)) if((pb == null) || (pb.Length != nCount))
{ {
if(m_strReadExcp != null) if(!string.IsNullOrEmpty(m_strReadExcp))
throw new IOException(m_strReadExcp); throw new EndOfStreamException(m_strReadExcp);
else else throw new EndOfStreamException();
throw new EndOfStreamException();
} }
if(m_sCopyTo != null) if(m_sCopyTo != null) m_sCopyTo.Write(pb, 0, pb.Length);
m_sCopyTo.Write(pb, 0, pb.Length);
return pb; return pb;
} }
catch(Exception) catch(Exception)
{ {
if(m_strReadExcp != null) if(!string.IsNullOrEmpty(m_strReadExcp))
throw new IOException(m_strReadExcp); throw new IOException(m_strReadExcp);
else throw; else throw;
} }
} }

View File

@@ -125,34 +125,26 @@ namespace ModernKeePassLib.Serialization
public static LockFileInfo Load(IOConnectionInfo iocLockFile) public static LockFileInfo Load(IOConnectionInfo iocLockFile)
{ {
using (var s = IOConnection.OpenRead(iocLockFile)) Stream s = null;
try try
{ {
s = IOConnection.OpenRead(iocLockFile);
if(s == null) return null; if(s == null) return null;
using (var sr = new StreamReader(s, StrUtil.Utf8)) StreamReader sr = new StreamReader(s, StrUtil.Utf8);
{ string str = sr.ReadToEnd();
string str = sr.ReadToEnd(); sr.Dispose();
if (str == null) if(str == null) { Debug.Assert(false); return null; }
{
Debug.Assert(false);
}
str = StrUtil.NormalizeNewLines(str, false); str = StrUtil.NormalizeNewLines(str, false);
string[] v = str.Split('\n'); string[] v = str.Split('\n');
if ((v == null) || (v.Length < 6)) if((v == null) || (v.Length < 6)) { Debug.Assert(false); return null; }
{
Debug.Assert(false);
}
if (!v[0].StartsWith(LockFileHeader)) if(!v[0].StartsWith(LockFileHeader)) { Debug.Assert(false); return null; }
{ return new LockFileInfo(v[1], v[2], v[3], v[4], v[5]);
Debug.Assert(false);
}
return new LockFileInfo(v[1], v[2], v[3], v[4], v[5]);
}
} }
catch(FileNotFoundException) { } catch(FileNotFoundException) { }
catch(Exception) { Debug.Assert(false); } catch(Exception) { Debug.Assert(false); }
finally { if(s != null) s.Dispose(); }
return null; return null;
} }
@@ -160,27 +152,48 @@ namespace ModernKeePassLib.Serialization
// Throws on error // Throws on error
public static LockFileInfo Create(IOConnectionInfo iocLockFile) public static LockFileInfo Create(IOConnectionInfo iocLockFile)
{ {
byte[] pbID = CryptoRandom.Instance.GetRandomBytes(16); LockFileInfo lfi;
string strTime = TimeUtil.SerializeUtc(DateTime.Now); Stream s = null;
try
var lfi = new LockFileInfo(Convert.ToBase64String(pbID), strTime,
string.Empty, string.Empty, string.Empty);
StringBuilder sb = new StringBuilder();
sb.AppendLine(LockFileHeader);
sb.AppendLine(lfi.ID);
sb.AppendLine(strTime);
sb.AppendLine(lfi.UserName);
sb.AppendLine(lfi.Machine);
sb.AppendLine(lfi.Domain);
using (var s = IOConnection.OpenWrite(iocLockFile))
{ {
byte[] pbID = CryptoRandom.Instance.GetRandomBytes(16);
string strTime = TimeUtil.SerializeUtc(DateTime.UtcNow);
lfi = new LockFileInfo(Convert.ToBase64String(pbID), strTime,
#if KeePassUAP
EnvironmentExt.UserName, EnvironmentExt.MachineName,
EnvironmentExt.UserDomainName);
#elif ModernKeePassLib|| KeePassLibSD
string.Empty, string.Empty, string.Empty);
#else
Environment.UserName, Environment.MachineName,
Environment.UserDomainName);
#endif
StringBuilder sb = new StringBuilder();
#if !KeePassLibSD
sb.AppendLine(LockFileHeader);
sb.AppendLine(lfi.ID);
sb.AppendLine(strTime);
sb.AppendLine(lfi.UserName);
sb.AppendLine(lfi.Machine);
sb.AppendLine(lfi.Domain);
#else
sb.Append(LockFileHeader + Environment.NewLine);
sb.Append(lfi.ID + Environment.NewLine);
sb.Append(strTime + Environment.NewLine);
sb.Append(lfi.UserName + Environment.NewLine);
sb.Append(lfi.Machine + Environment.NewLine);
sb.Append(lfi.Domain + Environment.NewLine);
#endif
byte[] pbFile = StrUtil.Utf8.GetBytes(sb.ToString()); byte[] pbFile = StrUtil.Utf8.GetBytes(sb.ToString());
if (s == null) throw new IOException(iocLockFile.GetDisplayName());
s.WriteAsync(pbFile, 0, pbFile.Length).GetAwaiter().GetResult(); s = IOConnection.OpenWrite(iocLockFile);
if(s == null) throw new IOException(iocLockFile.GetDisplayName());
s.Write(pbFile, 0, pbFile.Length);
} }
finally { if(s != null) s.Dispose(); }
return lfi; return lfi;
} }
@@ -241,8 +254,8 @@ namespace ModernKeePassLib.Serialization
#endif #endif
} }
if(bDisposing && !bFileDeleted) // if(bDisposing && !bFileDeleted)
IOConnection.DeleteFile(m_iocLockFile); // Possibly with exception // IOConnection.DeleteFile(m_iocLockFile); // Possibly with exception
m_iocLockFile = null; m_iocLockFile = null;
} }

View File

@@ -30,7 +30,9 @@ using System.Security.AccessControl;
using ModernKeePassLib.Native; using ModernKeePassLib.Native;
using ModernKeePassLib.Utility; using ModernKeePassLib.Utility;
using System.Threading.Tasks; using System.Threading.Tasks;
using Windows.Storage;
using Windows.Storage.Streams; using Windows.Storage.Streams;
using ModernKeePassLib.Resources;
namespace ModernKeePassLib.Serialization namespace ModernKeePassLib.Serialization
{ {
@@ -44,6 +46,16 @@ namespace ModernKeePassLib.Serialization
private const string StrTempSuffix = ".tmp"; private const string StrTempSuffix = ".tmp";
private static Dictionary<string, bool> g_dEnabled =
new Dictionary<string, bool>(StrUtil.CaseIgnoreComparer);
private static bool g_bExtraSafe = false;
internal static bool ExtraSafe
{
get { return g_bExtraSafe; }
set { g_bExtraSafe = value; }
}
public FileTransactionEx(IOConnectionInfo iocBaseFile) public FileTransactionEx(IOConnectionInfo iocBaseFile)
{ {
Initialize(iocBaseFile, true); Initialize(iocBaseFile, true);
@@ -61,16 +73,47 @@ namespace ModernKeePassLib.Serialization
m_bTransacted = bTransacted; m_bTransacted = bTransacted;
m_iocBase = iocBaseFile.CloneDeep(); m_iocBase = iocBaseFile.CloneDeep();
// ModernKeePassLib is currently targeting .NET 4.5 string strPath = m_iocBase.Path;
#if !ModernKeePassLib #if !ModernKeePassLib
if(m_iocBase.IsLocalFile())
{
try
{
if(File.Exists(strPath))
{
// Symbolic links are realized via reparse points;
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa365503.aspx
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa365680.aspx
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa365006.aspx
// Performing a file transaction on a symbolic link
// would delete/replace the symbolic link instead of
// writing to its target
FileAttributes fa = File.GetAttributes(strPath);
if((long)(fa & FileAttributes.ReparsePoint) != 0)
m_bTransacted = false;
}
}
catch(Exception) { Debug.Assert(false); }
}
// Prevent transactions for FTP URLs under .NET 4.0 in order to // Prevent transactions for FTP URLs under .NET 4.0 in order to
// avoid/workaround .NET bug 621450: // avoid/workaround .NET bug 621450:
// https://connect.microsoft.com/VisualStudio/feedback/details/621450/problem-renaming-file-on-ftp-server-using-ftpwebrequest-in-net-framework-4-0-vs2010-only // https://connect.microsoft.com/VisualStudio/feedback/details/621450/problem-renaming-file-on-ftp-server-using-ftpwebrequest-in-net-framework-4-0-vs2010-only
if(m_iocBase.Path.StartsWith("ftp:", StrUtil.CaseIgnoreCmp) && if(strPath.StartsWith("ftp:", StrUtil.CaseIgnoreCmp) &&
(Environment.Version.Major >= 4) && !NativeLib.IsUnix()) (Environment.Version.Major >= 4) && !NativeLib.IsUnix())
m_bTransacted = false; m_bTransacted = false;
#endif #endif
foreach(KeyValuePair<string, bool> kvp in g_dEnabled)
{
if(strPath.StartsWith(kvp.Key, StrUtil.CaseIgnoreCmp))
{
m_bTransacted = kvp.Value;
break;
}
}
if(m_bTransacted) if(m_bTransacted)
{ {
m_iocTemp = m_iocBase.CloneDeep(); m_iocTemp = m_iocBase.CloneDeep();
@@ -109,6 +152,13 @@ namespace ModernKeePassLib.Serialization
bool bEfsEncrypted = false; bool bEfsEncrypted = false;
#endif #endif
if(g_bExtraSafe)
{
if(!IOConnection.FileExists(m_iocTemp))
throw new FileNotFoundException(m_iocTemp.Path +
Environment.NewLine + KLRes.FileSaveFailed);
}
if(IOConnection.FileExists(m_iocBase)) if(IOConnection.FileExists(m_iocBase))
{ {
#if (!ModernKeePassLib && !KeePassLibSD && !KeePassRT) #if (!ModernKeePassLib && !KeePassLibSD && !KeePassRT)
@@ -119,10 +169,10 @@ namespace ModernKeePassLib.Serialization
FileAttributes faBase = File.GetAttributes(m_iocBase.Path); FileAttributes faBase = File.GetAttributes(m_iocBase.Path);
bEfsEncrypted = ((long)(faBase & FileAttributes.Encrypted) != 0); bEfsEncrypted = ((long)(faBase & FileAttributes.Encrypted) != 0);
DateTime tCreation = File.GetCreationTime(m_iocBase.Path); DateTime tCreation = File.GetCreationTimeUtc(m_iocBase.Path);
bkSecurity = File.GetAccessControl(m_iocBase.Path); bkSecurity = File.GetAccessControl(m_iocBase.Path);
File.SetCreationTime(m_iocTemp.Path, tCreation); File.SetCreationTimeUtc(m_iocTemp.Path, tCreation);
} }
catch(Exception) { Debug.Assert(false); } catch(Exception) { Debug.Assert(false); }
} }
@@ -153,5 +203,15 @@ namespace ModernKeePassLib.Serialization
if(bMadeUnhidden) UrlUtil.HideFile(m_iocBase.Path, true); // Hide again if(bMadeUnhidden) UrlUtil.HideFile(m_iocBase.Path, true); // Hide again
} }
// For plugins
public static void Configure(string strPrefix, bool? obTransacted)
{
if(string.IsNullOrEmpty(strPrefix)) { Debug.Assert(false); return; }
if(obTransacted.HasValue)
g_dEnabled[strPrefix] = obTransacted.Value;
else g_dEnabled.Remove(strPrefix);
}
} }
} }

View File

@@ -31,6 +31,7 @@ using System.Text;
using ModernKeePassLib.Native; using ModernKeePassLib.Native;
using ModernKeePassLib.Utility; using ModernKeePassLib.Utility;
using Windows.Security.Cryptography.Core; using Windows.Security.Cryptography.Core;
using ModernKeePassLib.Cryptography;
#if KeePassLibSD #if KeePassLibSD
using KeePassLibSD; using KeePassLibSD;
@@ -40,7 +41,7 @@ namespace ModernKeePassLib.Serialization
{ {
public sealed class HashedBlockStream : Stream public sealed class HashedBlockStream : Stream
{ {
private const int m_nDefaultBufferSize = 1024 * 1024; // 1 MB private const int NbDefaultBufferSize = 1024 * 1024; // 1 MB
private Stream m_sBaseStream; private Stream m_sBaseStream;
private bool m_bWriting; private bool m_bWriting;
@@ -53,7 +54,7 @@ namespace ModernKeePassLib.Serialization
private byte[] m_pbBuffer; private byte[] m_pbBuffer;
private int m_nBufferPos = 0; private int m_nBufferPos = 0;
private uint m_uBufferIndex = 0; private uint m_uBlockIndex = 0;
public override bool CanRead public override bool CanRead
{ {
@@ -72,13 +73,13 @@ namespace ModernKeePassLib.Serialization
public override long Length public override long Length
{ {
get { throw new NotSupportedException(); } get { Debug.Assert(false); throw new NotSupportedException(); }
} }
public override long Position public override long Position
{ {
get { throw new NotSupportedException(); } get { Debug.Assert(false); throw new NotSupportedException(); }
set { throw new NotSupportedException(); } set { Debug.Assert(false); throw new NotSupportedException(); }
} }
public HashedBlockStream(Stream sBaseStream, bool bWriting) public HashedBlockStream(Stream sBaseStream, bool bWriting)
@@ -100,29 +101,28 @@ namespace ModernKeePassLib.Serialization
private void Initialize(Stream sBaseStream, bool bWriting, int nBufferSize, private void Initialize(Stream sBaseStream, bool bWriting, int nBufferSize,
bool bVerify) bool bVerify)
{ {
if (sBaseStream != null) m_sBaseStream = sBaseStream; if(sBaseStream == null) throw new ArgumentNullException("sBaseStream");
else throw new ArgumentNullException(nameof(sBaseStream)); if(nBufferSize < 0) throw new ArgumentOutOfRangeException("nBufferSize");
if (nBufferSize < 0)
throw new ArgumentOutOfRangeException(nameof(nBufferSize));
if(nBufferSize == 0) if(nBufferSize == 0) nBufferSize = NbDefaultBufferSize;
nBufferSize = m_nDefaultBufferSize;
m_sBaseStream = sBaseStream;
m_bWriting = bWriting; m_bWriting = bWriting;
m_bVerify = bVerify; m_bVerify = bVerify;
UTF8Encoding utf8 = StrUtil.Utf8; UTF8Encoding utf8 = StrUtil.Utf8;
if(m_bWriting == false) // Reading mode if(!m_bWriting) // Reading mode
{ {
if(m_sBaseStream.CanRead == false) if(!m_sBaseStream.CanRead)
throw new InvalidOperationException(); throw new InvalidOperationException();
m_brInput = new BinaryReader(sBaseStream, utf8); m_brInput = new BinaryReader(sBaseStream, utf8);
m_pbBuffer = new byte[0]; m_pbBuffer = MemUtil.EmptyByteArray;
} }
else // Writing mode else // Writing mode
{ {
if(m_sBaseStream.CanWrite == false) if(!m_sBaseStream.CanWrite)
throw new InvalidOperationException(); throw new InvalidOperationException();
m_bwOutput = new BinaryWriter(sBaseStream, utf8); m_bwOutput = new BinaryWriter(sBaseStream, utf8);
@@ -131,25 +131,13 @@ namespace ModernKeePassLib.Serialization
} }
} }
public override void Flush()
{
if(m_bWriting) m_bwOutput.Flush();
}
#if ModernKeePassLib || KeePassRT
protected override void Dispose(bool disposing) protected override void Dispose(bool disposing)
{ {
if(!disposing) return; if(disposing && (m_sBaseStream != null))
#else
public override void Close()
{
#endif
if(m_sBaseStream != null)
{ {
if(m_bWriting == false) // Reading mode if(!m_bWriting) // Reading mode
{ {
try { m_brInput.Dispose(); } catch { } m_brInput.Dispose();
m_brInput = null; m_brInput = null;
} }
else // Writing mode else // Writing mode
@@ -167,9 +155,16 @@ namespace ModernKeePassLib.Serialization
m_bwOutput = null; m_bwOutput = null;
} }
try { m_sBaseStream.Dispose(); } catch { } m_sBaseStream.Dispose();
m_sBaseStream = null; m_sBaseStream = null;
} }
base.Dispose(disposing);
}
public override void Flush()
{
if(m_bWriting) m_bwOutput.Flush();
} }
public override long Seek(long lOffset, SeekOrigin soOrigin) public override long Seek(long lOffset, SeekOrigin soOrigin)
@@ -192,7 +187,7 @@ namespace ModernKeePassLib.Serialization
if(m_nBufferPos == m_pbBuffer.Length) if(m_nBufferPos == m_pbBuffer.Length)
{ {
if(ReadHashedBlock() == false) if(ReadHashedBlock() == false)
return nCount - nRemaining; // Bytes actually read return (nCount - nRemaining); // Bytes actually read
} }
int nCopy = Math.Min(m_pbBuffer.Length - m_nBufferPos, nRemaining); int nCopy = Math.Min(m_pbBuffer.Length - m_nBufferPos, nRemaining);
@@ -214,9 +209,9 @@ namespace ModernKeePassLib.Serialization
m_nBufferPos = 0; m_nBufferPos = 0;
if(m_brInput.ReadUInt32() != m_uBufferIndex) if(m_brInput.ReadUInt32() != m_uBlockIndex)
throw new InvalidDataException(); throw new InvalidDataException();
++m_uBufferIndex; ++m_uBlockIndex;
byte[] pbStoredHash = m_brInput.ReadBytes(32); byte[] pbStoredHash = m_brInput.ReadBytes(32);
if((pbStoredHash == null) || (pbStoredHash.Length != 32)) if((pbStoredHash == null) || (pbStoredHash.Length != 32))
@@ -241,7 +236,7 @@ namespace ModernKeePassLib.Serialization
} }
m_bEos = true; m_bEos = true;
m_pbBuffer = new byte[0]; m_pbBuffer = MemUtil.EmptyByteArray;
return false; return false;
} }
@@ -251,25 +246,12 @@ namespace ModernKeePassLib.Serialization
if(m_bVerify) if(m_bVerify)
{ {
#if ModernKeePassLib byte[] pbComputedHash = CryptoUtil.HashSha256(m_pbBuffer);
/*var sha256 = WinRTCrypto.HashAlgorithmProvider.OpenAlgorithm(HashAlgorithm.Sha256); if((pbComputedHash == null) || (pbComputedHash.Length != 32))
var pbComputedHash = sha256.HashData(m_pbBuffer);*/
var sha256 = HashAlgorithmProvider.OpenAlgorithm(HashAlgorithmNames.Sha256);
var buffer = sha256.HashData(CryptographicBuffer.CreateFromByteArray(m_pbBuffer));
byte[] pbComputedHash;
CryptographicBuffer.CopyToByteArray(buffer, out pbComputedHash);
#else
SHA256Managed sha256 = new SHA256Managed();
byte[] pbComputedHash = sha256.ComputeHash(m_pbBuffer);
#endif
if ((pbComputedHash == null) || (pbComputedHash.Length != 32))
throw new InvalidOperationException(); throw new InvalidOperationException();
for(int iHashPos = 0; iHashPos < 32; ++iHashPos) if(!MemUtil.ArraysEqual(pbStoredHash, pbComputedHash))
{ throw new InvalidDataException();
if(pbStoredHash[iHashPos] != pbComputedHash[iHashPos])
throw new InvalidDataException();
}
} }
return true; return true;
@@ -297,39 +279,26 @@ namespace ModernKeePassLib.Serialization
private void WriteHashedBlock() private void WriteHashedBlock()
{ {
m_bwOutput.Write(m_uBufferIndex); m_bwOutput.Write(m_uBlockIndex);
++m_uBufferIndex; ++m_uBlockIndex;
if(m_nBufferPos > 0) if(m_nBufferPos > 0)
{ {
#if ModernKeePassLib byte[] pbHash = CryptoUtil.HashSha256(m_pbBuffer, 0, m_nBufferPos);
/*var sha256 = WinRTCrypto.HashAlgorithmProvider.OpenAlgorithm(HashAlgorithm.Sha256);
var pbHash = sha256.HashData(m_pbBuffer.Where((x, i) => i < m_nBufferPos).ToArray());*/
var sha256 = HashAlgorithmProvider.OpenAlgorithm(HashAlgorithmNames.Sha256);
var buffer = sha256.HashData(CryptographicBuffer.CreateFromByteArray(m_pbBuffer.Where((x, i) => i < m_nBufferPos).ToArray()));
byte[] pbHash;
CryptographicBuffer.CopyToByteArray(buffer, out pbHash);
#else
SHA256Managed sha256 = new SHA256Managed(); // For KeePassLibSD:
// SHA256Managed sha256 = new SHA256Managed();
// byte[] pbHash;
// if(m_nBufferPos == m_pbBuffer.Length)
// pbHash = sha256.ComputeHash(m_pbBuffer);
// else
// {
// byte[] pbData = new byte[m_nBufferPos];
// Array.Copy(m_pbBuffer, 0, pbData, 0, m_nBufferPos);
// pbHash = sha256.ComputeHash(pbData);
// }
#if !KeePassLibSD m_bwOutput.Write(pbHash);
byte[] pbHash = sha256.ComputeHash(m_pbBuffer, 0, m_nBufferPos);
#else
byte[] pbHash;
if(m_nBufferPos == m_pbBuffer.Length)
pbHash = sha256.ComputeHash(m_pbBuffer);
else
{
byte[] pbData = new byte[m_nBufferPos];
Array.Copy(m_pbBuffer, 0, pbData, 0, m_nBufferPos);
pbHash = sha256.ComputeHash(pbData);
}
#endif
#endif
m_bwOutput.Write(pbHash);
} }
else else
{ {

View File

@@ -0,0 +1,356 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2017 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.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;
using Windows.Security.Cryptography;
using Windows.Security.Cryptography.Core;
using ModernKeePassLib.Resources;
using ModernKeePassLib.Utility;
using Org.BouncyCastle.Crypto.Digests;
using Org.BouncyCastle.Crypto.Macs;
namespace ModernKeePassLib.Serialization
{
public sealed class HmacBlockStream : Stream
{
private const int NbDefaultBufferSize = 1024 * 1024; // 1 MB
private Stream m_sBase;
private readonly bool m_bWriting;
private readonly bool m_bVerify;
private byte[] m_pbKey;
private bool m_bEos = false;
private byte[] m_pbBuffer;
private int m_iBufferPos = 0;
private ulong m_uBlockIndex = 0;
public override bool CanRead
{
get { return !m_bWriting; }
}
public override bool CanSeek
{
get { return false; }
}
public override bool CanWrite
{
get { return m_bWriting; }
}
public override long Length
{
get { Debug.Assert(false); throw new NotSupportedException(); }
}
public override long Position
{
get { Debug.Assert(false); throw new NotSupportedException(); }
set { Debug.Assert(false); throw new NotSupportedException(); }
}
public HmacBlockStream(Stream sBase, bool bWriting, bool bVerify,
byte[] pbKey)
{
if(sBase == null) throw new ArgumentNullException("sBase");
if(pbKey == null) throw new ArgumentNullException("pbKey");
m_sBase = sBase;
m_bWriting = bWriting;
m_bVerify = bVerify;
m_pbKey = pbKey;
if(!m_bWriting) // Reading mode
{
if(!m_sBase.CanRead) throw new InvalidOperationException();
m_pbBuffer = MemUtil.EmptyByteArray;
}
else // Writing mode
{
if(!m_sBase.CanWrite) throw new InvalidOperationException();
m_pbBuffer = new byte[NbDefaultBufferSize];
}
}
protected override void Dispose(bool disposing)
{
if(disposing && (m_sBase != null))
{
if(m_bWriting)
{
if(m_iBufferPos == 0) // No data left in buffer
WriteSafeBlock(); // Write terminating block
else
{
WriteSafeBlock(); // Write remaining buffered data
WriteSafeBlock(); // Write terminating block
}
Flush();
}
//m_sBase.Close();
m_sBase = null;
}
base.Dispose(disposing);
}
public override void Flush()
{
Debug.Assert(m_sBase != null); // Object should not be disposed
if(m_bWriting && (m_sBase != null)) m_sBase.Flush();
}
public override long Seek(long lOffset, SeekOrigin soOrigin)
{
Debug.Assert(false);
throw new NotSupportedException();
}
public override void SetLength(long lValue)
{
Debug.Assert(false);
throw new NotSupportedException();
}
internal static byte[] GetHmacKey64(byte[] pbKey, ulong uBlockIndex)
{
if(pbKey == null) throw new ArgumentNullException("pbKey");
Debug.Assert(pbKey.Length == 64);
// We are computing the HMAC using SHA-256, whose internal
// block size is 512 bits; thus create a key that is 512
// bits long (using SHA-512)
byte[] pbBlockKey = MemUtil.EmptyByteArray;
byte[] pbIndex = MemUtil.UInt64ToBytes(uBlockIndex);
var h = new Sha512Digest();
h.BlockUpdate(pbIndex, 0, pbIndex.Length);
h.BlockUpdate(pbKey, 0, pbKey.Length);
h.DoFinal(pbBlockKey, 0);
h.Reset();
/*byte[] pbBlockKey;
using(SHA512Managed h = new SHA512Managed())
{
byte[] pbIndex = MemUtil.UInt64ToBytes(uBlockIndex);
h.TransformBlock(pbIndex, 0, pbIndex.Length, pbIndex, 0);
h.TransformBlock(pbKey, 0, pbKey.Length, pbKey, 0);
h.TransformFinalBlock(MemUtil.EmptyByteArray, 0, 0);
pbBlockKey = h.Hash;
}
*/
#if DEBUG
byte[] pbZero = new byte[64];
Debug.Assert((pbBlockKey.Length == 64) && !MemUtil.ArraysEqual(
pbBlockKey, pbZero)); // Ensure we own pbBlockKey
#endif
return pbBlockKey;
}
public override int Read(byte[] pbBuffer, int iOffset, int nCount)
{
if(m_bWriting) throw new InvalidOperationException();
int nRemaining = nCount;
while(nRemaining > 0)
{
if(m_iBufferPos == m_pbBuffer.Length)
{
if(!ReadSafeBlock())
return (nCount - nRemaining); // Bytes actually read
}
int nCopy = Math.Min(m_pbBuffer.Length - m_iBufferPos, nRemaining);
Debug.Assert(nCopy > 0);
Array.Copy(m_pbBuffer, m_iBufferPos, pbBuffer, iOffset, nCopy);
iOffset += nCopy;
m_iBufferPos += nCopy;
nRemaining -= nCopy;
}
return nCount;
}
private bool ReadSafeBlock()
{
if(m_bEos) return false; // End of stream reached already
byte[] pbStoredHmac = MemUtil.Read(m_sBase, 32);
if((pbStoredHmac == null) || (pbStoredHmac.Length != 32))
throw new EndOfStreamException(KLRes.FileCorrupted + " " +
KLRes.FileIncomplete);
// Block index is implicit: it's used in the HMAC computation,
// but does not need to be stored
// byte[] pbBlockIndex = MemUtil.Read(m_sBase, 8);
// if((pbBlockIndex == null) || (pbBlockIndex.Length != 8))
// throw new EndOfStreamException();
// ulong uBlockIndex = MemUtil.BytesToUInt64(pbBlockIndex);
// if((uBlockIndex != m_uBlockIndex) && m_bVerify)
// throw new InvalidDataException();
byte[] pbBlockIndex = MemUtil.UInt64ToBytes(m_uBlockIndex);
byte[] pbBlockSize = MemUtil.Read(m_sBase, 4);
if((pbBlockSize == null) || (pbBlockSize.Length != 4))
throw new EndOfStreamException(KLRes.FileCorrupted + " " +
KLRes.FileIncomplete);
int nBlockSize = MemUtil.BytesToInt32(pbBlockSize);
if(nBlockSize < 0)
throw new InvalidDataException(KLRes.FileCorrupted);
m_iBufferPos = 0;
m_pbBuffer = MemUtil.Read(m_sBase, nBlockSize);
if((m_pbBuffer == null) || ((m_pbBuffer.Length != nBlockSize) && m_bVerify))
throw new EndOfStreamException(KLRes.FileCorrupted + " " +
KLRes.FileIncompleteExpc);
if(m_bVerify)
{
byte[] pbCmpHmac = MemUtil.EmptyByteArray;
byte[] pbBlockKey = GetHmacKey64(m_pbKey, m_uBlockIndex);
#if ModernKeePassLib
var h = new HMac(new Sha256Digest());
h.BlockUpdate(pbBlockIndex, 0, pbBlockIndex.Length);
h.BlockUpdate(pbBlockSize, 0, pbBlockSize.Length);
if (m_pbBuffer.Length > 0)
h.BlockUpdate(m_pbBuffer, 0, m_pbBuffer.Length);
h.DoFinal(pbCmpHmac, 0);
h.Reset();
#else
using(HMACSHA256 h = new HMACSHA256(pbBlockKey))
{
h.TransformBlock(pbBlockIndex, 0, pbBlockIndex.Length,
pbBlockIndex, 0);
h.TransformBlock(pbBlockSize, 0, pbBlockSize.Length,
pbBlockSize, 0);
if(m_pbBuffer.Length > 0)
h.TransformBlock(m_pbBuffer, 0, m_pbBuffer.Length,
m_pbBuffer, 0);
h.TransformFinalBlock(MemUtil.EmptyByteArray, 0, 0);
pbCmpHmac = h.Hash;
}
#endif
MemUtil.ZeroByteArray(pbBlockKey);
if(!MemUtil.ArraysEqual(pbCmpHmac, pbStoredHmac))
throw new InvalidDataException(KLRes.FileCorrupted);
}
++m_uBlockIndex;
if(nBlockSize == 0)
{
m_bEos = true;
return false; // No further data available
}
return true;
}
public override void Write(byte[] pbBuffer, int iOffset, int nCount)
{
if(!m_bWriting) throw new InvalidOperationException();
while(nCount > 0)
{
if(m_iBufferPos == m_pbBuffer.Length)
WriteSafeBlock();
int nCopy = Math.Min(m_pbBuffer.Length - m_iBufferPos, nCount);
Debug.Assert(nCopy > 0);
Array.Copy(pbBuffer, iOffset, m_pbBuffer, m_iBufferPos, nCopy);
iOffset += nCopy;
m_iBufferPos += nCopy;
nCount -= nCopy;
}
}
private void WriteSafeBlock()
{
byte[] pbBlockIndex = MemUtil.UInt64ToBytes(m_uBlockIndex);
int cbBlockSize = m_iBufferPos;
byte[] pbBlockSize = MemUtil.Int32ToBytes(cbBlockSize);
byte[] pbBlockHmac = MemUtil.EmptyByteArray;
byte[] pbBlockKey = GetHmacKey64(m_pbKey, m_uBlockIndex);
#if ModernKeePassLib
var h = new HMac(new Sha256Digest());
h.BlockUpdate(pbBlockIndex, 0, pbBlockIndex.Length);
h.BlockUpdate(pbBlockSize, 0, pbBlockSize.Length);
if (m_pbBuffer.Length > 0)
h.BlockUpdate(m_pbBuffer, 0, m_pbBuffer.Length);
h.DoFinal(pbBlockHmac, 0);
h.Reset();
#else
using(HMACSHA256 h = new HMACSHA256(pbBlockKey))
{
h.TransformBlock(pbBlockIndex, 0, pbBlockIndex.Length,
pbBlockIndex, 0);
h.TransformBlock(pbBlockSize, 0, pbBlockSize.Length,
pbBlockSize, 0);
if(cbBlockSize > 0)
h.TransformBlock(m_pbBuffer, 0, cbBlockSize, m_pbBuffer, 0);
h.TransformFinalBlock(MemUtil.EmptyByteArray, 0, 0);
pbBlockHmac = h.Hash;
}
#endif
MemUtil.ZeroByteArray(pbBlockKey);
MemUtil.Write(m_sBase, pbBlockHmac);
// MemUtil.Write(m_sBase, pbBlockIndex); // Implicit
MemUtil.Write(m_sBase, pbBlockSize);
if(cbBlockSize > 0)
m_sBase.Write(m_pbBuffer, 0, cbBlockSize);
++m_uBlockIndex;
m_iBufferPos = 0;
}
}
}

View File

@@ -24,6 +24,7 @@ using System.Net;
using System.Diagnostics; using System.Diagnostics;
using Windows.Storage.Streams; using Windows.Storage.Streams;
using System.Threading.Tasks; using System.Threading.Tasks;
using ModernKeePassLib.Native;
#if (!ModernKeePassLib && !KeePassLibSD && !KeePassRT) #if (!ModernKeePassLib && !KeePassLibSD && !KeePassRT)
using System.Net.Cache; using System.Net.Cache;
using System.Net.Security; using System.Net.Security;
@@ -35,7 +36,6 @@ using System.Security.Cryptography.X509Certificates;
#if ModernKeePassLib #if ModernKeePassLib
using Windows.Storage; using Windows.Storage;
//using PCLStorage;
#endif #endif
using ModernKeePassLib.Utility; using ModernKeePassLib.Utility;
@@ -112,6 +112,7 @@ namespace ModernKeePassLib.Serialization
m_s = sBase; m_s = sBase;
} }
#if !KeePassUAP
public override IAsyncResult BeginRead(byte[] buffer, int offset, public override IAsyncResult BeginRead(byte[] buffer, int offset,
int count, AsyncCallback callback, object state) int count, AsyncCallback callback, object state)
{ {
@@ -123,12 +124,16 @@ namespace ModernKeePassLib.Serialization
{ {
return BeginWrite(buffer, offset, count, callback, state); return BeginWrite(buffer, offset, count, callback, state);
} }
#endif
public override void Close() protected override void Dispose(bool disposing)
{ {
m_s.Close(); if(disposing) m_s.Dispose();
base.Dispose(disposing);
} }
#if !KeePassUAP
public override int EndRead(IAsyncResult asyncResult) public override int EndRead(IAsyncResult asyncResult)
{ {
return m_s.EndRead(asyncResult); return m_s.EndRead(asyncResult);
@@ -138,6 +143,7 @@ namespace ModernKeePassLib.Serialization
{ {
m_s.EndWrite(asyncResult); m_s.EndWrite(asyncResult);
} }
#endif
public override void Flush() public override void Flush()
{ {
@@ -178,17 +184,19 @@ namespace ModernKeePassLib.Serialization
internal sealed class IocStream : WrapperStream internal sealed class IocStream : WrapperStream
{ {
private readonly bool m_bWrite; // Initially opened for writing private readonly bool m_bWrite; // Initially opened for writing
private bool m_bDisposed = false;
public IocStream(Stream sBase) : base(sBase) public IocStream(Stream sBase) : base(sBase)
{ {
m_bWrite = sBase.CanWrite; m_bWrite = sBase.CanWrite;
} }
public override void Close() protected override void Dispose(bool disposing)
{ {
base.Close(); base.Dispose(disposing);
if(MonoWorkarounds.IsRequired(10163) && m_bWrite) if(disposing && MonoWorkarounds.IsRequired(10163) && m_bWrite &&
!m_bDisposed)
{ {
try try
{ {
@@ -210,6 +218,8 @@ namespace ModernKeePassLib.Serialization
} }
catch(Exception) { Debug.Assert(false); } catch(Exception) { Debug.Assert(false); }
} }
m_bDisposed = true;
} }
public static Stream WrapIfRequired(Stream s) public static Stream WrapIfRequired(Stream s)
@@ -230,15 +240,20 @@ namespace ModernKeePassLib.Serialization
private static ProxyServerType m_pstProxyType = ProxyServerType.System; private static ProxyServerType m_pstProxyType = ProxyServerType.System;
private static string m_strProxyAddr = string.Empty; private static string m_strProxyAddr = string.Empty;
private static string m_strProxyPort = string.Empty; private static string m_strProxyPort = string.Empty;
private static ProxyAuthType m_patProxyAuthType = ProxyAuthType.Auto;
private static string m_strProxyUserName = string.Empty; private static string m_strProxyUserName = string.Empty;
private static string m_strProxyPassword = string.Empty; private static string m_strProxyPassword = string.Empty;
#if !KeePassUAP
private static bool? m_obDefaultExpect100Continue = null;
private static bool m_bSslCertsAcceptInvalid = false; private static bool m_bSslCertsAcceptInvalid = false;
internal static bool SslCertsAcceptInvalid internal static bool SslCertsAcceptInvalid
{ {
// get { return m_bSslCertsAcceptInvalid; } // get { return m_bSslCertsAcceptInvalid; }
set { m_bSslCertsAcceptInvalid = value; } set { m_bSslCertsAcceptInvalid = value; }
} }
#endif
#endif #endif
// Web request methods // Web request methods
@@ -260,31 +275,67 @@ namespace ModernKeePassLib.Serialization
} }
internal static void SetProxy(ProxyServerType pst, string strAddr, internal static void SetProxy(ProxyServerType pst, string strAddr,
string strPort, string strUserName, string strPassword) string strPort, ProxyAuthType pat, string strUserName,
string strPassword)
{ {
m_pstProxyType = pst; m_pstProxyType = pst;
m_strProxyAddr = (strAddr ?? string.Empty); m_strProxyAddr = (strAddr ?? string.Empty);
m_strProxyPort = (strPort ?? string.Empty); m_strProxyPort = (strPort ?? string.Empty);
m_patProxyAuthType = pat;
m_strProxyUserName = (strUserName ?? string.Empty); m_strProxyUserName = (strUserName ?? string.Empty);
m_strProxyPassword = (strPassword ?? string.Empty); m_strProxyPassword = (strPassword ?? string.Empty);
} }
internal static void ConfigureWebRequest(WebRequest request) internal static void ConfigureWebRequest(WebRequest request,
IOConnectionInfo ioc)
{ {
if(request == null) { Debug.Assert(false); return; } // No throw if(request == null) { Debug.Assert(false); return; } // No throw
// WebDAV support IocProperties p = ((ioc != null) ? ioc.Properties : null);
if(request is HttpWebRequest) if(p == null) { Debug.Assert(false); p = new IocProperties(); }
{
request.PreAuthenticate = true; // Also auth GET
if(request.Method == WebRequestMethods.Http.Post)
request.Method = WebRequestMethods.Http.Put;
}
// else if(request is FtpWebRequest)
// {
// Debug.Assert(((FtpWebRequest)request).UsePassive);
// }
IHasIocProperties ihpReq = (request as IHasIocProperties);
if(ihpReq != null)
{
IocProperties pEx = ihpReq.IOConnectionProperties;
if(pEx != null) p.CopyTo(pEx);
else ihpReq.IOConnectionProperties = p.CloneDeep();
}
if(IsHttpWebRequest(request))
{
// WebDAV support
#if !KeePassUAP
request.PreAuthenticate = true; // Also auth GET
#endif
if(string.Equals(request.Method, WebRequestMethods.Http.Post,
StrUtil.CaseIgnoreCmp))
request.Method = WebRequestMethods.Http.Put;
#if !KeePassUAP
HttpWebRequest hwr = (request as HttpWebRequest);
if(hwr != null)
{
string strUA = p.Get(IocKnownProperties.UserAgent);
if(!string.IsNullOrEmpty(strUA)) hwr.UserAgent = strUA;
}
else { Debug.Assert(false); }
#endif
}
#if !KeePassUAP
else if(IsFtpWebRequest(request))
{
FtpWebRequest fwr = (request as FtpWebRequest);
if(fwr != null)
{
bool? obPassive = p.GetBool(IocKnownProperties.Passive);
if(obPassive.HasValue) fwr.UsePassive = obPassive.Value;
}
else { Debug.Assert(false); }
}
#endif
#if !KeePassUAP
// Not implemented and ignored in Mono < 2.10 // Not implemented and ignored in Mono < 2.10
try try
{ {
@@ -292,6 +343,7 @@ namespace ModernKeePassLib.Serialization
} }
catch(NotImplementedException) { } catch(NotImplementedException) { }
catch(Exception) { Debug.Assert(false); } catch(Exception) { Debug.Assert(false); }
#endif
try try
{ {
@@ -299,10 +351,20 @@ namespace ModernKeePassLib.Serialization
if(GetWebProxy(out prx)) request.Proxy = prx; if(GetWebProxy(out prx)) request.Proxy = prx;
} }
catch(Exception) { Debug.Assert(false); } catch(Exception) { Debug.Assert(false); }
#if !KeePassUAP
long? olTimeout = p.GetLong(IocKnownProperties.Timeout);
if(olTimeout.HasValue && (olTimeout.Value >= 0))
request.Timeout = (int)Math.Min(olTimeout.Value, (long)int.MaxValue);
bool? ob = p.GetBool(IocKnownProperties.PreAuth);
if(ob.HasValue) request.PreAuthenticate = ob.Value;
#endif
} }
internal static void ConfigureWebClient(WebClient wc) internal static void ConfigureWebClient(WebClient wc)
{ {
#if !KeePassUAP
// Not implemented and ignored in Mono < 2.10 // Not implemented and ignored in Mono < 2.10
try try
{ {
@@ -310,6 +372,7 @@ namespace ModernKeePassLib.Serialization
} }
catch(NotImplementedException) { } catch(NotImplementedException) { }
catch(Exception) { Debug.Assert(false); } catch(Exception) { Debug.Assert(false); }
#endif
try try
{ {
@@ -320,67 +383,163 @@ namespace ModernKeePassLib.Serialization
} }
private static bool GetWebProxy(out IWebProxy prx) private static bool GetWebProxy(out IWebProxy prx)
{
bool b = GetWebProxyServer(out prx);
if(b) AssignCredentials(prx);
return b;
}
private static bool GetWebProxyServer(out IWebProxy prx)
{ {
prx = null; prx = null;
if(m_pstProxyType == ProxyServerType.None) if(m_pstProxyType == ProxyServerType.None)
return true; // Use null proxy return true; // Use null proxy
if(m_pstProxyType == ProxyServerType.Manual) if(m_pstProxyType == ProxyServerType.Manual)
{ {
try try
{ {
if(m_strProxyPort.Length > 0) if(m_strProxyAddr.Length == 0)
{
// First try default (from config), then system
prx = WebRequest.DefaultWebProxy;
#if !KeePassUAP
if(prx == null) prx = WebRequest.GetSystemWebProxy();
#endif
}
else if(m_strProxyPort.Length > 0)
prx = new WebProxy(m_strProxyAddr, int.Parse(m_strProxyPort)); prx = new WebProxy(m_strProxyAddr, int.Parse(m_strProxyPort));
else prx = new WebProxy(m_strProxyAddr); else prx = new WebProxy(m_strProxyAddr);
if((m_strProxyUserName.Length > 0) || (m_strProxyPassword.Length > 0)) return (prx != null);
prx.Credentials = new NetworkCredential(m_strProxyUserName,
m_strProxyPassword);
return true; // Use manual proxy
} }
catch(Exception exProxy) #if KeePassUAP
catch(Exception) { Debug.Assert(false); }
#else
catch(Exception ex)
{ {
string strInfo = m_strProxyAddr; string strInfo = m_strProxyAddr;
if(m_strProxyPort.Length > 0) strInfo += ":" + m_strProxyPort; if(m_strProxyPort.Length > 0)
MessageService.ShowWarning(strInfo, exProxy.Message); strInfo += ":" + m_strProxyPort;
MessageService.ShowWarning(strInfo, ex.Message);
} }
#endif
return false; // Use default return false; // Use default
} }
if((m_strProxyUserName.Length == 0) && (m_strProxyPassword.Length == 0)) Debug.Assert(m_pstProxyType == ProxyServerType.System);
return false; // Use default proxy, no auth
try try
{ {
prx = WebRequest.DefaultWebProxy; // First try system, then default (from config)
if(prx == null) prx = WebRequest.GetSystemWebProxy(); #if !KeePassUAP
if(prx == null) throw new InvalidOperationException(); prx = WebRequest.GetSystemWebProxy();
#endif
if(prx == null) prx = WebRequest.DefaultWebProxy;
prx.Credentials = new NetworkCredential(m_strProxyUserName, return (prx != null);
m_strProxyPassword);
return true;
} }
catch(Exception) { Debug.Assert(false); } catch(Exception) { Debug.Assert(false); }
return false; return false;
} }
private static void PrepareWebAccess() private static void AssignCredentials(IWebProxy prx)
{ {
if(m_bSslCertsAcceptInvalid) if(prx == null) return; // No assert
ServicePointManager.ServerCertificateValidationCallback =
IOConnection.AcceptCertificate; string strUserName = m_strProxyUserName;
else string strPassword = m_strProxyPassword;
ServicePointManager.ServerCertificateValidationCallback = null;
ProxyAuthType pat = m_patProxyAuthType;
if(pat == ProxyAuthType.Auto)
{
if((strUserName.Length > 0) || (strPassword.Length > 0))
pat = ProxyAuthType.Manual;
else pat = ProxyAuthType.Default;
}
try
{
if(pat == ProxyAuthType.None)
prx.Credentials = null;
else if(pat == ProxyAuthType.Default)
prx.Credentials = CredentialCache.DefaultCredentials;
else if(pat == ProxyAuthType.Manual)
{
if((strUserName.Length > 0) || (strPassword.Length > 0))
prx.Credentials = new NetworkCredential(
strUserName, strPassword);
}
else { Debug.Assert(false); }
}
catch(Exception) { Debug.Assert(false); }
}
private static void PrepareWebAccess(IOConnectionInfo ioc)
{
#if !KeePassUAP
IocProperties p = ((ioc != null) ? ioc.Properties : null);
if(p == null) { Debug.Assert(false); p = new IocProperties(); }
try
{
if(m_bSslCertsAcceptInvalid)
ServicePointManager.ServerCertificateValidationCallback =
IOConnection.AcceptCertificate;
else
ServicePointManager.ServerCertificateValidationCallback = null;
}
catch(Exception) { Debug.Assert(false); }
try
{
SecurityProtocolType spt = (SecurityProtocolType.Ssl3 |
SecurityProtocolType.Tls);
// The flags Tls11 and Tls12 in SecurityProtocolType have been
// introduced in .NET 4.5 and must not be set when running under
// older .NET versions (otherwise an exception is thrown)
Type tSpt = typeof(SecurityProtocolType);
string[] vSpt = Enum.GetNames(tSpt);
foreach(string strSpt in vSpt)
{
if(strSpt.Equals("Tls11", StrUtil.CaseIgnoreCmp))
spt |= (SecurityProtocolType)Enum.Parse(tSpt, "Tls11", true);
else if(strSpt.Equals("Tls12", StrUtil.CaseIgnoreCmp))
spt |= (SecurityProtocolType)Enum.Parse(tSpt, "Tls12", true);
}
ServicePointManager.SecurityProtocol = spt;
}
catch(Exception) { Debug.Assert(false); }
try
{
bool bCurCont = ServicePointManager.Expect100Continue;
if(!m_obDefaultExpect100Continue.HasValue)
{
Debug.Assert(bCurCont); // Default should be true
m_obDefaultExpect100Continue = bCurCont;
}
bool bNewCont = m_obDefaultExpect100Continue.Value;
bool? ob = p.GetBool(IocKnownProperties.Expect100Continue);
if(ob.HasValue) bNewCont = ob.Value;
if(bNewCont != bCurCont)
ServicePointManager.Expect100Continue = bNewCont;
}
catch(Exception) { Debug.Assert(false); }
#endif
} }
private static IOWebClient CreateWebClient(IOConnectionInfo ioc) private static IOWebClient CreateWebClient(IOConnectionInfo ioc)
{ {
PrepareWebAccess(); PrepareWebAccess(ioc);
IOWebClient wc = new IOWebClient(); IOWebClient wc = new IOWebClient(ioc);
ConfigureWebClient(wc); ConfigureWebClient(wc);
if((ioc.UserName.Length > 0) || (ioc.Password.Length > 0)) if((ioc.UserName.Length > 0) || (ioc.Password.Length > 0))
@@ -393,10 +552,10 @@ namespace ModernKeePassLib.Serialization
private static WebRequest CreateWebRequest(IOConnectionInfo ioc) private static WebRequest CreateWebRequest(IOConnectionInfo ioc)
{ {
PrepareWebAccess(); PrepareWebAccess(ioc);
WebRequest req = WebRequest.Create(ioc.Path); WebRequest req = WebRequest.Create(ioc.Path);
ConfigureWebRequest(req); ConfigureWebRequest(req, ioc);
if((ioc.UserName.Length > 0) || (ioc.Password.Length > 0)) if((ioc.UserName.Length > 0) || (ioc.Password.Length > 0))
req.Credentials = new NetworkCredential(ioc.UserName, ioc.Password); req.Credentials = new NetworkCredential(ioc.UserName, ioc.Password);
@@ -422,7 +581,7 @@ namespace ModernKeePassLib.Serialization
new Uri(ioc.Path))); new Uri(ioc.Path)));
} }
#else #else
public static Stream OpenRead(IOConnectionInfo ioc) public static Stream OpenRead(IOConnectionInfo ioc)
{ {
RaiseIOAccessPreEvent(ioc, IOAccessType.Read); RaiseIOAccessPreEvent(ioc, IOAccessType.Read);
@@ -449,9 +608,7 @@ namespace ModernKeePassLib.Serialization
// Mono does not set HttpWebRequest.Method to POST for writes, // Mono does not set HttpWebRequest.Method to POST for writes,
// so one needs to set the method to PUT explicitly // so one needs to set the method to PUT explicitly
if(NativeLib.IsUnix() && (uri.Scheme.Equals(Uri.UriSchemeHttp, if(NativeLib.IsUnix() && IsHttpWebRequest(uri))
StrUtil.CaseIgnoreCmp) || uri.Scheme.Equals(Uri.UriSchemeHttps,
StrUtil.CaseIgnoreCmp)))
s = CreateWebClient(ioc).OpenWrite(uri, WebRequestMethods.Http.Put); s = CreateWebClient(ioc).OpenWrite(uri, WebRequestMethods.Http.Put);
else s = CreateWebClient(ioc).OpenWrite(uri); else s = CreateWebClient(ioc).OpenWrite(uri);
@@ -478,8 +635,7 @@ namespace ModernKeePassLib.Serialization
public static bool FileExists(IOConnectionInfo ioc, bool bThrowErrors) public static bool FileExists(IOConnectionInfo ioc, bool bThrowErrors)
{ {
if(ioc == null) { Debug.Assert(false); if(ioc == null) { Debug.Assert(false); return false; }
}
RaiseIOAccessPreEvent(ioc, IOAccessType.Exists); RaiseIOAccessPreEvent(ioc, IOAccessType.Exists);
@@ -526,7 +682,7 @@ namespace ModernKeePassLib.Serialization
} }
#endif #endif
#if !ModernKeePassLib #if !ModernKeePassLib
internal static void DisposeResponse(WebResponse wr, bool bGetStream) internal static void DisposeResponse(WebResponse wr, bool bGetStream)
{ {
if(wr == null) return; if(wr == null) return;
@@ -535,26 +691,25 @@ namespace ModernKeePassLib.Serialization
if(bGetStream) if(bGetStream)
{ {
Stream s = wr.GetResponseStream(); Stream s = wr.GetResponseStream();
if(s != null) s.Dispose(); if(s != null) s.Close();
} }
} }
catch(Exception) { Debug.Assert(false); } catch(Exception) { Debug.Assert(false); }
try { wr.Dispose(); } try { wr.Close(); }
catch(Exception) { Debug.Assert(false); } catch(Exception) { Debug.Assert(false); }
} }
#endif #endif
public static byte[] ReadFile(IOConnectionInfo ioc) public static byte[] ReadFile(IOConnectionInfo ioc)
{ {
Stream sIn = null; Stream sIn = null;
MemoryStream ms = null; MemoryStream ms = null;
try try
{ {
sIn = OpenRead(ioc); sIn = IOConnection.OpenRead(ioc);
if(sIn == null) return null; if(sIn == null) return null;
ms = new MemoryStream(); ms = new MemoryStream();
MemUtil.CopyStream(sIn, ms); MemUtil.CopyStream(sIn, ms);
return ms.ToArray(); return ms.ToArray();
@@ -587,5 +742,49 @@ namespace ModernKeePassLib.Serialization
IOConnection.IOAccessPre(null, e); IOConnection.IOAccessPre(null, e);
} }
} }
#if !ModernKeePassLib
private static bool IsHttpWebRequest(Uri uri)
{
if(uri == null) { Debug.Assert(false); return false; }
string sch = uri.Scheme;
if(sch == null) { Debug.Assert(false); return false; }
return (sch.Equals("http", StrUtil.CaseIgnoreCmp) || // Uri.UriSchemeHttp
sch.Equals("https", StrUtil.CaseIgnoreCmp)); // Uri.UriSchemeHttps
}
internal static bool IsHttpWebRequest(WebRequest wr)
{
if(wr == null) { Debug.Assert(false); return false; }
#if KeePassUAP
return IsHttpWebRequest(wr.RequestUri);
#else
return (wr is HttpWebRequest);
#endif
}
internal static bool IsFtpWebRequest(WebRequest wr)
{
if(wr == null) { Debug.Assert(false); return false; }
#if KeePassUAP
return string.Equals(wr.RequestUri.Scheme, "ftp", StrUtil.CaseIgnoreCmp);
#else
return (wr is FtpWebRequest);
#endif
}
private static bool IsFileWebRequest(WebRequest wr)
{
if(wr == null) { Debug.Assert(false); return false; }
#if KeePassUAP
return string.Equals(wr.RequestUri.Scheme, "file", StrUtil.CaseIgnoreCmp);
#else
return (wr is FileWebRequest);
#endif
}
#endif // ModernKeePass
} }
} }

View File

@@ -139,9 +139,40 @@ namespace ModernKeePassLib.Serialization
set { m_ioHint = value; } set { m_ioHint = value; }
} */ } */
private IocProperties m_props = new IocProperties();
[XmlIgnore]
public IocProperties Properties
{
get { return m_props; }
set
{
if(value == null) throw new ArgumentNullException("value");
m_props = value;
}
}
/// <summary>
/// For serialization only; use <c>Properties</c> in code.
/// </summary>
[DefaultValue("")]
public string PropertiesEx
{
get { return m_props.Serialize(); }
set
{
if(value == null) throw new ArgumentNullException("value");
IocProperties p = IocProperties.Deserialize(value);
Debug.Assert(p != null);
m_props = (p ?? new IocProperties());
}
}
public IOConnectionInfo CloneDeep() public IOConnectionInfo CloneDeep()
{ {
return (IOConnectionInfo)this.MemberwiseClone(); IOConnectionInfo ioc = (IOConnectionInfo)this.MemberwiseClone();
ioc.m_props = m_props.CloneDeep();
return ioc;
} }
#if DEBUG // For debugger display only #if DEBUG // For debugger display only
@@ -274,14 +305,14 @@ namespace ModernKeePassLib.Serialization
string str = m_strUrl; string str = m_strUrl;
if(m_strUser.Length > 0) if(m_strUser.Length > 0)
str += " (" + m_strUser + ")"; str += (" (" + m_strUser + ")");
return str; return str;
} }
public bool IsEmpty() public bool IsEmpty()
{ {
return (m_strUrl.Length > 0); return (m_strUrl.Length == 0);
} }
public static IOConnectionInfo FromPath(string strPath) public static IOConnectionInfo FromPath(string strPath)
@@ -320,13 +351,13 @@ namespace ModernKeePassLib.Serialization
if(IsLocalFile()) return File.Exists(m_strUrl); if(IsLocalFile()) return File.Exists(m_strUrl);
#endif #endif
return true; return true;
} }
public bool IsLocalFile() public bool IsLocalFile()
{ {
// Not just ":/", see e.g. AppConfigEx.ChangePathRelAbs // Not just ":/", see e.g. AppConfigEx.ChangePathRelAbs
return (m_strUrl.IndexOf(@"://") < 0); return (m_strUrl.IndexOf("://") < 0);
} }
public void ClearCredentials(bool bDependingOnRememberMode) public void ClearCredentials(bool bDependingOnRememberMode)

View File

@@ -0,0 +1,192 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2017 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.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Text;
using System.Xml;
using ModernKeePassLib.Interfaces;
using ModernKeePassLib.Utility;
using StrDict = System.Collections.Generic.Dictionary<string, string>;
namespace ModernKeePassLib.Serialization
{
public interface IHasIocProperties
{
IocProperties IOConnectionProperties { get; set; }
}
public sealed class IocProperties : IDeepCloneable<IocProperties>
{
private StrDict m_dict = new StrDict();
public IocProperties()
{
}
public IocProperties CloneDeep()
{
IocProperties p = new IocProperties();
p.m_dict = new StrDict(m_dict);
return p;
}
public string Get(string strKey)
{
if(string.IsNullOrEmpty(strKey)) return null;
foreach(KeyValuePair<string, string> kvp in m_dict)
{
if(kvp.Key.Equals(strKey, StrUtil.CaseIgnoreCmp))
return kvp.Value;
}
return null;
}
public void Set(string strKey, string strValue)
{
if(string.IsNullOrEmpty(strKey)) { Debug.Assert(false); return; }
foreach(KeyValuePair<string, string> kvp in m_dict)
{
if(kvp.Key.Equals(strKey, StrUtil.CaseIgnoreCmp))
{
if(string.IsNullOrEmpty(strValue)) m_dict.Remove(kvp.Key);
else m_dict[kvp.Key] = strValue;
return;
}
}
if(!string.IsNullOrEmpty(strValue)) m_dict[strKey] = strValue;
}
public bool? GetBool(string strKey)
{
string str = Get(strKey);
if(string.IsNullOrEmpty(str)) return null;
return StrUtil.StringToBool(str);
}
public void SetBool(string strKey, bool? ob)
{
if(ob.HasValue) Set(strKey, (ob.Value ? "1" : "0"));
else Set(strKey, null);
}
public long? GetLong(string strKey)
{
string str = Get(strKey);
if(string.IsNullOrEmpty(str)) return null;
long l;
if(StrUtil.TryParseLongInvariant(str, out l)) return l;
Debug.Assert(false);
return null;
}
public void SetLong(string strKey, long? ol)
{
if(ol.HasValue)
Set(strKey, ol.Value.ToString(NumberFormatInfo.InvariantInfo));
else Set(strKey, null);
}
public string Serialize()
{
if(m_dict.Count == 0) return string.Empty;
StringBuilder sbAll = new StringBuilder();
foreach(KeyValuePair<string, string> kvp in m_dict)
{
sbAll.Append(kvp.Key);
sbAll.Append(kvp.Value);
}
string strAll = sbAll.ToString();
char chSepOuter = ';';
if(strAll.IndexOf(chSepOuter) >= 0)
chSepOuter = StrUtil.GetUnusedChar(strAll);
strAll += chSepOuter;
char chSepInner = '=';
if(strAll.IndexOf(chSepInner) >= 0)
chSepInner = StrUtil.GetUnusedChar(strAll);
StringBuilder sb = new StringBuilder();
sb.Append(chSepOuter);
sb.Append(chSepInner);
foreach(KeyValuePair<string, string> kvp in m_dict)
{
sb.Append(chSepOuter);
sb.Append(kvp.Key);
sb.Append(chSepInner);
sb.Append(kvp.Value);
}
return sb.ToString();
}
public static IocProperties Deserialize(string strSerialized)
{
IocProperties p = new IocProperties();
if(string.IsNullOrEmpty(strSerialized)) return p; // No assert
char chSepOuter = strSerialized[0];
string[] v = strSerialized.Substring(1).Split(new char[] { chSepOuter });
if((v == null) || (v.Length < 2)) { Debug.Assert(false); return p; }
string strMeta = v[0];
if(string.IsNullOrEmpty(strMeta)) { Debug.Assert(false); return p; }
char chSepInner = strMeta[0];
char[] vSepInner = new char[] { chSepInner };
for(int i = 1; i < v.Length; ++i)
{
string strProp = v[i];
if(string.IsNullOrEmpty(strProp)) { Debug.Assert(false); continue; }
string[] vProp = strProp.Split(vSepInner);
if((vProp == null) || (vProp.Length < 2)) { Debug.Assert(false); continue; }
Debug.Assert(vProp.Length == 2);
p.Set(vProp[0], vProp[1]);
}
return p;
}
public void CopyTo(IocProperties p)
{
if(p == null) { Debug.Assert(false); return; }
foreach(KeyValuePair<string, string> kvp in m_dict)
{
p.m_dict[kvp.Key] = kvp.Value;
}
}
}
}

View File

@@ -0,0 +1,99 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2017 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.Collections.Generic;
using System.Diagnostics;
using System.Text;
using ModernKeePassLib.Utility;
namespace ModernKeePassLib.Serialization
{
public sealed class IocPropertyInfo
{
private readonly string m_strName;
public string Name
{
get { return m_strName; }
}
private readonly Type m_t;
public Type Type
{
get { return m_t; }
}
private string m_strDisplayName;
public string DisplayName
{
get { return m_strDisplayName; }
set
{
if(value == null) throw new ArgumentNullException("value");
m_strDisplayName = value;
}
}
private List<string> m_lProtocols = new List<string>();
public IEnumerable<string> Protocols
{
get { return m_lProtocols; }
}
public IocPropertyInfo(string strName, Type t, string strDisplayName,
string[] vProtocols)
{
if(strName == null) throw new ArgumentNullException("strName");
if(t == null) throw new ArgumentNullException("t");
if(strDisplayName == null) throw new ArgumentNullException("strDisplayName");
m_strName = strName;
m_t = t;
m_strDisplayName = strDisplayName;
AddProtocols(vProtocols);
}
public void AddProtocols(string[] v)
{
if(v == null) { Debug.Assert(false); return; }
foreach(string strProtocol in v)
{
if(strProtocol == null) continue;
string str = strProtocol.Trim();
if(str.Length == 0) continue;
bool bFound = false;
foreach(string strEx in m_lProtocols)
{
if(strEx.Equals(str, StrUtil.CaseIgnoreCmp))
{
bFound = true;
break;
}
}
if(!bFound) m_lProtocols.Add(str);
}
}
}
}

View File

@@ -0,0 +1,123 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2017 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.Collections.Generic;
using System.Diagnostics;
using System.Text;
using ModernKeePassLib.Resources;
using ModernKeePassLib.Utility;
namespace ModernKeePassLib.Serialization
{
public static class IocKnownProtocols
{
public const string Http = "HTTP";
public const string Https = "HTTPS";
public const string WebDav = "WebDAV";
public const string Ftp = "FTP";
}
public static class IocKnownProperties
{
public const string Timeout = "Timeout";
public const string PreAuth = "PreAuth";
public const string UserAgent = "UserAgent";
public const string Expect100Continue = "Expect100Continue";
public const string Passive = "Passive";
}
public static class IocPropertyInfoPool
{
private static List<IocPropertyInfo> m_l = null;
public static IEnumerable<IocPropertyInfo> PropertyInfos
{
get { EnsureInitialized(); return m_l; }
}
private static void EnsureInitialized()
{
if(m_l != null) return;
string strGen = KLRes.General;
string strHttp = IocKnownProtocols.Http;
string strHttps = IocKnownProtocols.Https;
string strWebDav = IocKnownProtocols.WebDav;
string strFtp = IocKnownProtocols.Ftp;
string[] vGen = new string[] { strGen };
string[] vHttp = new string[] { strHttp, strHttps, strWebDav };
string[] vFtp = new string[] { strFtp };
List<IocPropertyInfo> l = new List<IocPropertyInfo>();
l.Add(new IocPropertyInfo(IocKnownProperties.Timeout,
typeof(long), KLRes.Timeout + " [ms]", vGen));
l.Add(new IocPropertyInfo(IocKnownProperties.PreAuth,
typeof(bool), KLRes.PreAuth, vGen));
l.Add(new IocPropertyInfo(IocKnownProperties.UserAgent,
typeof(string), KLRes.UserAgent, vHttp));
l.Add(new IocPropertyInfo(IocKnownProperties.Expect100Continue,
typeof(bool), KLRes.Expect100Continue, vHttp));
l.Add(new IocPropertyInfo(IocKnownProperties.Passive,
typeof(bool), KLRes.Passive, vFtp));
// l.Add(new IocPropertyInfo("Test", typeof(bool),
// "Long long long long long long long long long long long long long long long long long long long long",
// new string[] { "Proto 1/9", "Proto 2/9", "Proto 3/9", "Proto 4/9", "Proto 5/9",
// "Proto 6/9", "Proto 7/9", "Proto 8/9", "Proto 9/9" }));
m_l = l;
}
public static IocPropertyInfo Get(string strName)
{
if(string.IsNullOrEmpty(strName)) { Debug.Assert(false); return null; }
EnsureInitialized();
foreach(IocPropertyInfo pi in m_l)
{
if(pi.Name.Equals(strName, StrUtil.CaseIgnoreCmp))
return pi;
}
return null;
}
public static bool Add(IocPropertyInfo pi)
{
if(pi == null) { Debug.Assert(false); return false; }
// Name must be non-empty
string strName = pi.Name;
if(string.IsNullOrEmpty(strName)) { Debug.Assert(false); return false; }
IocPropertyInfo piEx = Get(strName); // Ensures initialized
if(piEx != null) { Debug.Assert(false); return false; } // Exists already
m_l.Add(pi);
return true;
}
}
}

View File

@@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2014 Dominik Reichl <dominik.reichl@t-online.de> Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -19,6 +19,8 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text; using System.Text;
using System.Security; using System.Security;
using System.Drawing; using System.Drawing;
@@ -58,13 +60,17 @@ namespace ModernKeePassLib.Serialization
DeletedObject, DeletedObject,
Group, Group,
GroupTimes, GroupTimes,
GroupCustomData,
GroupCustomDataItem,
Entry, Entry,
EntryTimes, EntryTimes,
EntryString, EntryString,
EntryBinary, EntryBinary,
EntryAutoType, EntryAutoType,
EntryAutoTypeItem, EntryAutoTypeItem,
EntryHistory EntryHistory,
EntryCustomData,
EntryCustomDataItem
} }
private bool m_bReadNextNode = true; private bool m_bReadNextNode = true;
@@ -84,10 +90,14 @@ namespace ModernKeePassLib.Serialization
private byte[] m_pbCustomIconData = null; private byte[] m_pbCustomIconData = null;
private string m_strCustomDataKey = null; private string m_strCustomDataKey = null;
private string m_strCustomDataValue = null; private string m_strCustomDataValue = null;
private string m_strGroupCustomDataKey = null;
private string m_strGroupCustomDataValue = null;
private string m_strEntryCustomDataKey = null;
private string m_strEntryCustomDataValue = null;
private void ReadXmlStreamed(Stream readerStream, Stream sParentStream) private void ReadXmlStreamed(Stream sXml, Stream sParent)
{ {
ReadDocumentStreamed(CreateXmlReader(readerStream), sParentStream); ReadDocumentStreamed(CreateXmlReader(sXml), sParent);
} }
internal static XmlReaderSettings CreateStdXmlReaderSettings() internal static XmlReaderSettings CreateStdXmlReaderSettings()
@@ -125,7 +135,6 @@ namespace ModernKeePassLib.Serialization
if(xr == null) throw new ArgumentNullException("xr"); if(xr == null) throw new ArgumentNullException("xr");
m_ctxGroups.Clear(); m_ctxGroups.Clear();
m_dictBinPool = new Dictionary<string, ProtectedBinary>();
KdbContext ctx = KdbContext.Null; KdbContext ctx = KdbContext.Null;
@@ -216,15 +225,25 @@ namespace ModernKeePassLib.Serialization
ReadString(xr); // Ignore ReadString(xr); // Ignore
else if(xr.Name == ElemHeaderHash) else if(xr.Name == ElemHeaderHash)
{ {
// The header hash is typically only stored in
// KDBX <= 3.1 files, not in KDBX >= 4 files
// (here, the header is verified via a HMAC),
// but we also support it for KDBX >= 4 files
// (i.e. if it's present, we check it)
string strHash = ReadString(xr); string strHash = ReadString(xr);
if(!string.IsNullOrEmpty(strHash) && (m_pbHashOfHeader != null) && if(!string.IsNullOrEmpty(strHash) && (m_pbHashOfHeader != null) &&
!m_bRepairMode) !m_bRepairMode)
{ {
Debug.Assert(m_uFileVersion < FileVersion32_4);
byte[] pbHash = Convert.FromBase64String(strHash); byte[] pbHash = Convert.FromBase64String(strHash);
if(!MemUtil.ArraysEqual(pbHash, m_pbHashOfHeader)) if(!MemUtil.ArraysEqual(pbHash, m_pbHashOfHeader))
throw new IOException(KLRes.FileCorrupted); throw new InvalidDataException(KLRes.FileCorrupted);
} }
} }
else if(xr.Name == ElemSettingsChanged)
m_pwDatabase.SettingsChanged = ReadTime(xr);
else if(xr.Name == ElemDbName) else if(xr.Name == ElemDbName)
m_pwDatabase.Name = ReadString(xr); m_pwDatabase.Name = ReadString(xr);
else if(xr.Name == ElemDbNameChanged) else if(xr.Name == ElemDbNameChanged)
@@ -251,6 +270,8 @@ namespace ModernKeePassLib.Serialization
m_pwDatabase.MasterKeyChangeRec = ReadLong(xr, -1); m_pwDatabase.MasterKeyChangeRec = ReadLong(xr, -1);
else if(xr.Name == ElemDbKeyChangeForce) else if(xr.Name == ElemDbKeyChangeForce)
m_pwDatabase.MasterKeyChangeForce = ReadLong(xr, -1); m_pwDatabase.MasterKeyChangeForce = ReadLong(xr, -1);
else if(xr.Name == ElemDbKeyChangeForceOnce)
m_pwDatabase.MasterKeyChangeForceOnce = ReadBool(xr, false);
else if(xr.Name == ElemMemoryProt) else if(xr.Name == ElemMemoryProt)
return SwitchContext(ctx, KdbContext.MemoryProtection, xr); return SwitchContext(ctx, KdbContext.MemoryProtection, xr);
else if(xr.Name == ElemCustomIcons) else if(xr.Name == ElemCustomIcons)
@@ -323,7 +344,14 @@ namespace ModernKeePassLib.Serialization
string strKey = xr.Value; string strKey = xr.Value;
ProtectedBinary pbData = ReadProtectedBinary(xr); ProtectedBinary pbData = ReadProtectedBinary(xr);
m_dictBinPool[strKey ?? string.Empty] = pbData; int iKey;
if(!StrUtil.TryParseIntInvariant(strKey, out iKey))
throw new FormatException();
if(iKey < 0) throw new FormatException();
Debug.Assert(m_pbsBinaries.Get(iKey) == null);
Debug.Assert(m_pbsBinaries.Find(pbData) < 0);
m_pbsBinaries.Set(iKey, pbData);
} }
else ReadUnknown(xr); else ReadUnknown(xr);
} }
@@ -369,7 +397,7 @@ namespace ModernKeePassLib.Serialization
else if(xr.Name == ElemNotes) else if(xr.Name == ElemNotes)
m_ctxGroup.Notes = ReadString(xr); m_ctxGroup.Notes = ReadString(xr);
else if(xr.Name == ElemIcon) else if(xr.Name == ElemIcon)
m_ctxGroup.IconId = (PwIcon)ReadInt(xr, (int)PwIcon.Folder); m_ctxGroup.IconId = ReadIconId(xr, PwIcon.Folder);
else if(xr.Name == ElemCustomIconID) else if(xr.Name == ElemCustomIconID)
m_ctxGroup.CustomIconUuid = ReadUuid(xr); m_ctxGroup.CustomIconUuid = ReadUuid(xr);
else if(xr.Name == ElemTimes) else if(xr.Name == ElemTimes)
@@ -384,6 +412,8 @@ namespace ModernKeePassLib.Serialization
m_ctxGroup.EnableSearching = StrUtil.StringToBoolEx(ReadString(xr)); m_ctxGroup.EnableSearching = StrUtil.StringToBoolEx(ReadString(xr));
else if(xr.Name == ElemLastTopVisibleEntry) else if(xr.Name == ElemLastTopVisibleEntry)
m_ctxGroup.LastTopVisibleEntry = ReadUuid(xr); m_ctxGroup.LastTopVisibleEntry = ReadUuid(xr);
else if(xr.Name == ElemCustomData)
return SwitchContext(ctx, KdbContext.GroupCustomData, xr);
else if(xr.Name == ElemGroup) else if(xr.Name == ElemGroup)
{ {
m_ctxGroup = new PwGroup(false, false); m_ctxGroup = new PwGroup(false, false);
@@ -404,11 +434,25 @@ namespace ModernKeePassLib.Serialization
else ReadUnknown(xr); else ReadUnknown(xr);
break; break;
case KdbContext.GroupCustomData:
if(xr.Name == ElemStringDictExItem)
return SwitchContext(ctx, KdbContext.GroupCustomDataItem, xr);
else ReadUnknown(xr);
break;
case KdbContext.GroupCustomDataItem:
if(xr.Name == ElemKey)
m_strGroupCustomDataKey = ReadString(xr);
else if(xr.Name == ElemValue)
m_strGroupCustomDataValue = ReadString(xr);
else ReadUnknown(xr);
break;
case KdbContext.Entry: case KdbContext.Entry:
if(xr.Name == ElemUuid) if(xr.Name == ElemUuid)
m_ctxEntry.Uuid = ReadUuid(xr); m_ctxEntry.Uuid = ReadUuid(xr);
else if(xr.Name == ElemIcon) else if(xr.Name == ElemIcon)
m_ctxEntry.IconId = (PwIcon)ReadInt(xr, (int)PwIcon.Key); m_ctxEntry.IconId = ReadIconId(xr, PwIcon.Key);
else if(xr.Name == ElemCustomIconID) else if(xr.Name == ElemCustomIconID)
m_ctxEntry.CustomIconUuid = ReadUuid(xr); m_ctxEntry.CustomIconUuid = ReadUuid(xr);
else if(xr.Name == ElemFgColor) else if(xr.Name == ElemFgColor)
@@ -435,6 +479,8 @@ namespace ModernKeePassLib.Serialization
return SwitchContext(ctx, KdbContext.EntryBinary, xr); return SwitchContext(ctx, KdbContext.EntryBinary, xr);
else if(xr.Name == ElemAutoType) else if(xr.Name == ElemAutoType)
return SwitchContext(ctx, KdbContext.EntryAutoType, xr); return SwitchContext(ctx, KdbContext.EntryAutoType, xr);
else if(xr.Name == ElemCustomData)
return SwitchContext(ctx, KdbContext.EntryCustomData, xr);
else if(xr.Name == ElemHistory) else if(xr.Name == ElemHistory)
{ {
Debug.Assert(m_bEntryInHistory == false); Debug.Assert(m_bEntryInHistory == false);
@@ -509,6 +555,20 @@ namespace ModernKeePassLib.Serialization
else ReadUnknown(xr); else ReadUnknown(xr);
break; break;
case KdbContext.EntryCustomData:
if(xr.Name == ElemStringDictExItem)
return SwitchContext(ctx, KdbContext.EntryCustomDataItem, xr);
else ReadUnknown(xr);
break;
case KdbContext.EntryCustomDataItem:
if(xr.Name == ElemKey)
m_strEntryCustomDataKey = ReadString(xr);
else if(xr.Name == ElemValue)
m_strEntryCustomDataValue = ReadString(xr);
else ReadUnknown(xr);
break;
case KdbContext.EntryHistory: case KdbContext.EntryHistory:
if(xr.Name == ElemEntry) if(xr.Name == ElemEntry)
{ {
@@ -610,6 +670,19 @@ namespace ModernKeePassLib.Serialization
} }
else if((ctx == KdbContext.GroupTimes) && (xr.Name == ElemTimes)) else if((ctx == KdbContext.GroupTimes) && (xr.Name == ElemTimes))
return KdbContext.Group; return KdbContext.Group;
else if((ctx == KdbContext.GroupCustomData) && (xr.Name == ElemCustomData))
return KdbContext.Group;
else if((ctx == KdbContext.GroupCustomDataItem) && (xr.Name == ElemStringDictExItem))
{
if((m_strGroupCustomDataKey != null) && (m_strGroupCustomDataValue != null))
m_ctxGroup.CustomData.Set(m_strGroupCustomDataKey, m_strGroupCustomDataValue);
else { Debug.Assert(false); }
m_strGroupCustomDataKey = null;
m_strGroupCustomDataValue = null;
return KdbContext.GroupCustomData;
}
else if((ctx == KdbContext.Entry) && (xr.Name == ElemEntry)) else if((ctx == KdbContext.Entry) && (xr.Name == ElemEntry))
{ {
// Create new UUID if absent // Create new UUID if absent
@@ -660,6 +733,19 @@ namespace ModernKeePassLib.Serialization
m_ctxATSeq = null; m_ctxATSeq = null;
return KdbContext.EntryAutoType; return KdbContext.EntryAutoType;
} }
else if((ctx == KdbContext.EntryCustomData) && (xr.Name == ElemCustomData))
return KdbContext.Entry;
else if((ctx == KdbContext.EntryCustomDataItem) && (xr.Name == ElemStringDictExItem))
{
if((m_strEntryCustomDataKey != null) && (m_strEntryCustomDataValue != null))
m_ctxEntry.CustomData.Set(m_strEntryCustomDataKey, m_strEntryCustomDataValue);
else { Debug.Assert(false); }
m_strEntryCustomDataKey = null;
m_strEntryCustomDataValue = null;
return KdbContext.EntryCustomData;
}
else if((ctx == KdbContext.EntryHistory) && (xr.Name == ElemHistory)) else if((ctx == KdbContext.EntryHistory) && (xr.Name == ElemHistory))
{ {
m_bEntryInHistory = false; m_bEntryInHistory = false;
@@ -707,6 +793,55 @@ namespace ModernKeePassLib.Serialization
#endif #endif
} }
private byte[] ReadBase64(XmlReader xr, bool bRaw)
{
// if(bRaw) return ReadBase64RawInChunks(xr);
string str = (bRaw ? ReadStringRaw(xr) : ReadString(xr));
if(string.IsNullOrEmpty(str)) return MemUtil.EmptyByteArray;
return Convert.FromBase64String(str);
}
/* private byte[] m_pbBase64ReadBuf = new byte[1024 * 1024 * 3];
private byte[] ReadBase64RawInChunks(XmlReader xr)
{
xr.MoveToContent();
List<byte[]> lParts = new List<byte[]>();
byte[] pbBuf = m_pbBase64ReadBuf;
while(true)
{
int cb = xr.ReadElementContentAsBase64(pbBuf, 0, pbBuf.Length);
if(cb == 0) break;
byte[] pb = new byte[cb];
Array.Copy(pbBuf, 0, pb, 0, cb);
lParts.Add(pb);
// No break when cb < pbBuf.Length, because ReadElementContentAsBase64
// moves to the next XML node only when returning 0
}
m_bReadNextNode = false;
if(lParts.Count == 0) return MemUtil.EmptyByteArray;
if(lParts.Count == 1) return lParts[0];
long cbRes = 0;
for(int i = 0; i < lParts.Count; ++i)
cbRes += lParts[i].Length;
byte[] pbRes = new byte[cbRes];
int cbCur = 0;
for(int i = 0; i < lParts.Count; ++i)
{
Array.Copy(lParts[i], 0, pbRes, cbCur, lParts[i].Length);
cbCur += lParts[i].Length;
}
return pbRes;
} */
private bool ReadBool(XmlReader xr, bool bDefault) private bool ReadBool(XmlReader xr, bool bDefault)
{ {
string str = ReadString(xr); string str = ReadString(xr);
@@ -719,9 +854,9 @@ namespace ModernKeePassLib.Serialization
private PwUuid ReadUuid(XmlReader xr) private PwUuid ReadUuid(XmlReader xr)
{ {
string str = ReadString(xr); byte[] pb = ReadBase64(xr, false);
if(string.IsNullOrEmpty(str)) return PwUuid.Zero; if(pb.Length == 0) return PwUuid.Zero;
return new PwUuid(Convert.FromBase64String(str)); return new PwUuid(pb);
} }
private int ReadInt(XmlReader xr, int nDefault) private int ReadInt(XmlReader xr, int nDefault)
@@ -782,15 +917,44 @@ namespace ModernKeePassLib.Serialization
private DateTime ReadTime(XmlReader xr) private DateTime ReadTime(XmlReader xr)
{ {
string str = ReadString(xr); // Cf. WriteObject(string, DateTime)
if((m_format == KdbxFormat.Default) && (m_uFileVersion >= FileVersion32_4))
{
// long l = ReadLong(xr, -1);
// if(l != -1) return DateTime.FromBinary(l);
DateTime dt; byte[] pb = ReadBase64(xr, false);
if(TimeUtil.TryDeserializeUtc(str, out dt)) return dt; if(pb.Length != 8)
{
Debug.Assert(false);
byte[] pb8 = new byte[8];
Array.Copy(pb, pb8, Math.Min(pb.Length, 8)); // Little-endian
pb = pb8;
}
long lSec = MemUtil.BytesToInt64(pb);
return new DateTime(lSec * TimeSpan.TicksPerSecond, DateTimeKind.Utc);
}
else
{
string str = ReadString(xr);
DateTime dt;
if(TimeUtil.TryDeserializeUtc(str, out dt)) return dt;
}
Debug.Assert(false); Debug.Assert(false);
return m_dtNow; return m_dtNow;
} }
private PwIcon ReadIconId(XmlReader xr, PwIcon icDefault)
{
int i = ReadInt(xr, (int)icDefault);
if((i >= 0) && (i < (int)PwIcon.Count)) return (PwIcon)i;
Debug.Assert(false);
return icDefault;
}
private ProtectedString ReadProtectedString(XmlReader xr) private ProtectedString ReadProtectedString(XmlReader xr)
{ {
XorredBuffer xb = ProcessNode(xr); XorredBuffer xb = ProcessNode(xr);
@@ -815,10 +979,27 @@ namespace ModernKeePassLib.Serialization
if(xr.MoveToAttribute(AttrRef)) if(xr.MoveToAttribute(AttrRef))
{ {
string strRef = xr.Value; string strRef = xr.Value;
if(strRef != null) if(!string.IsNullOrEmpty(strRef))
{ {
ProtectedBinary pb = BinPoolGet(strRef); int iRef;
if(pb != null) return pb; if(StrUtil.TryParseIntInvariant(strRef, out iRef))
{
ProtectedBinary pb = m_pbsBinaries.Get(iRef);
if(pb != null)
{
// https://sourceforge.net/p/keepass/feature-requests/2023/
xr.MoveToElement();
#if DEBUG
string strInner = ReadStringRaw(xr);
Debug.Assert(string.IsNullOrEmpty(strInner));
#else
ReadStringRaw(xr);
#endif
return pb;
}
else { Debug.Assert(false); }
}
else { Debug.Assert(false); } else { Debug.Assert(false); }
} }
else { Debug.Assert(false); } else { Debug.Assert(false); }
@@ -835,10 +1016,9 @@ namespace ModernKeePassLib.Serialization
return new ProtectedBinary(true, xb); return new ProtectedBinary(true, xb);
} }
string strValue = ReadString(xr); byte[] pbData = ReadBase64(xr, true);
if(strValue.Length == 0) return new ProtectedBinary(); if(pbData.Length == 0) return new ProtectedBinary();
byte[] pbData = Convert.FromBase64String(strValue);
if(bCompressed) pbData = MemUtil.Decompress(pbData); if(bCompressed) pbData = MemUtil.Decompress(pbData);
return new ProtectedBinary(false, pbData); return new ProtectedBinary(false, pbData);
} }
@@ -875,13 +1055,8 @@ namespace ModernKeePassLib.Serialization
if(xr.Value == ValTrue) if(xr.Value == ValTrue)
{ {
xr.MoveToElement(); xr.MoveToElement();
string strEncrypted = ReadStringRaw(xr);
byte[] pbEncrypted;
if(strEncrypted.Length > 0)
pbEncrypted = Convert.FromBase64String(strEncrypted);
else pbEncrypted = new byte[0];
byte[] pbEncrypted = ReadBase64(xr, true);
byte[] pbPad = m_randomStream.GetRandomBytes((uint)pbEncrypted.Length); byte[] pbPad = m_randomStream.GetRandomBytes((uint)pbEncrypted.Length);
xb = new XorredBuffer(pbEncrypted, pbPad); xb = new XorredBuffer(pbEncrypted, pbPad);

View File

@@ -44,6 +44,9 @@ using ModernKeePassLib.Resources;
using ModernKeePassLib.Utility; using ModernKeePassLib.Utility;
using Windows.Security.Cryptography.Core; using Windows.Security.Cryptography.Core;
using Windows.Storage.Streams; using Windows.Storage.Streams;
using ModernKeePassLib.Collections;
using ModernKeePassLib.Cryptography.KeyDerivation;
using ModernKeePassLib.Security;
namespace ModernKeePassLib.Serialization namespace ModernKeePassLib.Serialization
{ {
@@ -53,85 +56,141 @@ namespace ModernKeePassLib.Serialization
public sealed partial class KdbxFile public sealed partial class KdbxFile
{ {
/// <summary> /// <summary>
/// Load a KDB file from a file. /// Load a KDBX file.
/// </summary> /// </summary>
/// <param name="strFilePath">File to load.</param> /// <param name="strFilePath">File to load.</param>
/// <param name="kdbFormat">Format specifier.</param> /// <param name="fmt">Format.</param>
/// <param name="slLogger">Status logger (optional).</param> /// <param name="slLogger">Status logger (optional).</param>
public void Load(string strFilePath, KdbxFormat kdbFormat, IStatusLogger slLogger) public void Load(string strFilePath, KdbxFormat fmt, IStatusLogger slLogger)
{ {
IOConnectionInfo ioc = IOConnectionInfo.FromPath(strFilePath); IOConnectionInfo ioc = IOConnectionInfo.FromPath(strFilePath);
Load(IOConnection.OpenRead(ioc), kdbFormat, slLogger); Load(IOConnection.OpenRead(ioc), fmt, slLogger);
} }
/// <summary> /// <summary>
/// Load a KDB file from a stream. /// Load a KDBX file from a stream.
/// </summary> /// </summary>
/// <param name="sSource">Stream to read the data from. Must contain /// <param name="sSource">Stream to read the data from. Must contain
/// a KDBX stream.</param> /// a KDBX stream.</param>
/// <param name="kdbFormat">Format specifier.</param> /// <param name="fmt">Format.</param>
/// <param name="slLogger">Status logger (optional).</param> /// <param name="slLogger">Status logger (optional).</param>
public void Load(Stream sSource, KdbxFormat kdbFormat, IStatusLogger slLogger) public void Load(Stream sSource, KdbxFormat fmt, IStatusLogger slLogger)
{ {
Debug.Assert(sSource != null); Debug.Assert(sSource != null);
if(sSource == null) throw new ArgumentNullException("sSource"); if(sSource == null) throw new ArgumentNullException("sSource");
m_format = kdbFormat; 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_slLogger = slLogger;
HashingStreamEx hashedStream = new HashingStreamEx(sSource, false, null); m_pbsBinaries.Clear();
UTF8Encoding encNoBom = StrUtil.Utf8; 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 try
{ {
BinaryReaderEx br = null; Stream sXml;
BinaryReaderEx brDecrypted = null; if(fmt == KdbxFormat.Default)
Stream readerStream = null;
if(kdbFormat == KdbxFormat.Default)
{ {
br = new BinaryReaderEx(hashedStream, encNoBom, KLRes.FileCorrupted); BinaryReaderEx br = new BinaryReaderEx(sHashing,
ReadHeader(br); encNoBom, KLRes.FileCorrupted);
byte[] pbHeader = LoadHeader(br);
m_pbHashOfHeader = CryptoUtil.HashSha256(pbHeader);
Stream sDecrypted = AttachStreamDecryptor(hashedStream); int cbEncKey, cbEncIV;
if((sDecrypted == null) || (sDecrypted == hashedStream)) ICipherEngine iCipher = GetCipher(out cbEncKey, out cbEncIV);
throw new SecurityException(KLRes.CryptoStreamFailed);
brDecrypted = new BinaryReaderEx(sDecrypted, encNoBom, KLRes.FileCorrupted); ComputeKeys(out pbCipherKey, cbEncKey, out pbHmacKey64);
byte[] pbStoredStartBytes = brDecrypted.ReadBytes(32);
if((m_pbStreamStartBytes == null) || (m_pbStreamStartBytes.Length != 32)) string strIncomplete = KLRes.FileHeaderCorrupted + " " +
throw new InvalidDataException(); KLRes.FileIncomplete;
for(int iStart = 0; iStart < 32; ++iStart) Stream sPlain;
if(m_uFileVersion < FileVersion32_4)
{ {
if(pbStoredStartBytes[iStart] != m_pbStreamStartBytes[iStart]) Stream sDecrypted = EncryptStream(sHashing, iCipher,
throw new InvalidCompositeKeyException(); pbCipherKey, cbEncIV, false);
} if((sDecrypted == null) || (sDecrypted == sHashing))
throw new SecurityException(KLRes.CryptoStreamFailed);
lStreams.Add(sDecrypted);
Stream sHashed = new HashedBlockStream(sDecrypted, false, 0, BinaryReaderEx brDecrypted = new BinaryReaderEx(sDecrypted,
!m_bRepairMode); 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) if(m_pwDatabase.Compression == PwCompressionAlgorithm.GZip)
readerStream = new GZipStream(sHashed, CompressionMode.Decompress); {
else readerStream = sHashed; sXml = new GZipStream(sPlain, CompressionMode.Decompress);
} lStreams.Add(sXml);
else if(kdbFormat == KdbxFormat.PlainXml) }
readerStream = hashedStream; else sXml = sPlain;
else { Debug.Assert(false); throw new FormatException("KdbFormat"); }
if(kdbFormat != KdbxFormat.PlainXml) // Is an encrypted format 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_pbProtectedStreamKey == null) if(m_pbInnerRandomStreamKey == null)
{ {
Debug.Assert(false); Debug.Assert(false);
throw new SecurityException("Invalid protected stream key!"); throw new SecurityException("Invalid inner random stream key!");
} }
m_randomStream = new CryptoRandomStream(m_craInnerRandomStream, m_randomStream = new CryptoRandomStream(m_craInnerRandomStream,
m_pbProtectedStreamKey); m_pbInnerRandomStreamKey);
} }
else m_randomStream = null; // No random stream for plain-text files
#if KeePassDebug_WriteXml #if KeePassDebug_WriteXml
// FileStream fsOut = new FileStream("Raw.xml", FileMode.Create, // FileStream fsOut = new FileStream("Raw.xml", FileMode.Create,
@@ -140,7 +199,7 @@ namespace ModernKeePassLib.Serialization
// { // {
// while(true) // while(true)
// { // {
// int b = readerStream.ReadByte(); // int b = sXml.ReadByte();
// if(b == -1) break; // if(b == -1) break;
// fsOut.WriteByte((byte)b); // fsOut.WriteByte((byte)b);
// } // }
@@ -149,12 +208,8 @@ namespace ModernKeePassLib.Serialization
// fsOut.Close(); // fsOut.Close();
#endif #endif
ReadXmlStreamed(readerStream, hashedStream); ReadXmlStreamed(sXml, sHashing);
// ReadXmlDom(readerStream); // ReadXmlDom(sXml);
readerStream.Dispose();
// GC.KeepAlive(br);
// GC.KeepAlive(brDecrypted);
} }
#if !ModernKeePassLib #if !ModernKeePassLib
catch(CryptographicException) // Thrown on invalid padding catch(CryptographicException) // Thrown on invalid padding
@@ -162,15 +217,30 @@ namespace ModernKeePassLib.Serialization
throw new CryptographicException(KLRes.FileCorrupted); throw new CryptographicException(KLRes.FileCorrupted);
} }
#endif #endif
finally { CommonCleanUpRead(sSource, hashedStream); } 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
} }
private void CommonCleanUpRead(Stream sSource, HashingStreamEx hashedStream) private void CommonCleanUpRead(List<Stream> lStreams, HashingStreamEx sHashing)
{ {
hashedStream.Dispose(); CloseStreams(lStreams);
m_pbHashOfFileOnDisk = hashedStream.Hash;
sSource.Dispose(); Debug.Assert(lStreams.Contains(sHashing)); // sHashing must be closed
m_pbHashOfFileOnDisk = sHashing.Hash;
Debug.Assert(m_pbHashOfFileOnDisk != null);
CleanUpInnerRandomStream();
// Reset memory protection settings (to always use reasonable // Reset memory protection settings (to always use reasonable
// defaults) // defaults)
@@ -183,11 +253,21 @@ namespace ModernKeePassLib.Serialization
// the history maintenance settings) // the history maintenance settings)
m_pwDatabase.MaintainBackups(); // Don't mark database as modified m_pwDatabase.MaintainBackups(); // Don't mark database as modified
// Expand the root group, such that in case the user accidently
// collapses the root group he can simply reopen the database
PwGroup pgRoot = m_pwDatabase.RootGroup;
if(pgRoot != null) pgRoot.IsExpanded = true;
else { Debug.Assert(false); }
m_pbHashOfHeader = null; m_pbHashOfHeader = null;
} }
private void ReadHeader(BinaryReaderEx br) private byte[] LoadHeader(BinaryReaderEx br)
{ {
string strPrevExcpText = br.ReadExceptionText;
br.ReadExceptionText = KLRes.FileHeaderCorrupted + " " +
KLRes.FileIncompleteExpc;
MemoryStream msHeader = new MemoryStream(); MemoryStream msHeader = new MemoryStream();
Debug.Assert(br.CopyDataTo == null); Debug.Assert(br.CopyDataTo == null);
br.CopyDataTo = msHeader; br.CopyDataTo = msHeader;
@@ -210,29 +290,21 @@ namespace ModernKeePassLib.Serialization
uint uVersion = MemUtil.BytesToUInt32(pb); uint uVersion = MemUtil.BytesToUInt32(pb);
if((uVersion & FileVersionCriticalMask) > (FileVersion32 & FileVersionCriticalMask)) if((uVersion & FileVersionCriticalMask) > (FileVersion32 & FileVersionCriticalMask))
throw new FormatException(KLRes.FileVersionUnsupported + throw new FormatException(KLRes.FileVersionUnsupported +
Environment.NewLine + Environment.NewLine + KLRes.FileNewVerReq); Environment.NewLine + KLRes.FileNewVerReq);
m_uFileVersion = uVersion;
while(true) while(true)
{ {
if(ReadHeaderField(br) == false) if(!ReadHeaderField(br)) break;
break;
} }
br.CopyDataTo = null; br.CopyDataTo = null;
byte[] pbHeader = msHeader.ToArray(); byte[] pbHeader = msHeader.ToArray();
msHeader.Dispose(); msHeader.Dispose();
#if ModernKeePassLib br.ReadExceptionText = strPrevExcpText;
/*var sha256 = WinRTCrypto.HashAlgorithmProvider.OpenAlgorithm(HashAlgorithm.Sha256); return pbHeader;
m_pbHashOfHeader = sha256.HashData(pbHeader);*/ }
var sha256 = HashAlgorithmProvider.OpenAlgorithm(HashAlgorithmNames.Sha256);
var buffer = sha256.HashData(CryptographicBuffer.CreateFromByteArray(pbHeader));
CryptographicBuffer.CopyToByteArray(buffer, out m_pbHashOfHeader);
#else
SHA256Managed sha256 = new SHA256Managed();
m_pbHashOfHeader = sha256.ComputeHash(pbHeader);
#endif
}
private bool ReadHeaderField(BinaryReaderEx brSource) private bool ReadHeaderField(BinaryReaderEx brSource)
{ {
@@ -240,18 +312,16 @@ namespace ModernKeePassLib.Serialization
if(brSource == null) throw new ArgumentNullException("brSource"); if(brSource == null) throw new ArgumentNullException("brSource");
byte btFieldID = brSource.ReadByte(); byte btFieldID = brSource.ReadByte();
ushort uSize = MemUtil.BytesToUInt16(brSource.ReadBytes(2));
byte[] pbData = null; int cbSize;
if(uSize > 0) Debug.Assert(m_uFileVersion > 0);
{ if(m_uFileVersion < FileVersion32_4)
string strPrevExcpText = brSource.ReadExceptionText; cbSize = (int)MemUtil.BytesToUInt16(brSource.ReadBytes(2));
brSource.ReadExceptionText = KLRes.FileHeaderEndEarly; else cbSize = MemUtil.BytesToInt32(brSource.ReadBytes(4));
if(cbSize < 0) throw new FormatException(KLRes.FileCorrupted);
pbData = brSource.ReadBytes(uSize); byte[] pbData = MemUtil.EmptyByteArray;
if(cbSize > 0) pbData = brSource.ReadBytes(cbSize);
brSource.ReadExceptionText = strPrevExcpText;
}
bool bResult = true; bool bResult = true;
KdbxHeaderFieldID kdbID = (KdbxHeaderFieldID)btFieldID; KdbxHeaderFieldID kdbID = (KdbxHeaderFieldID)btFieldID;
@@ -274,32 +344,63 @@ namespace ModernKeePassLib.Serialization
CryptoRandom.Instance.AddEntropy(pbData); CryptoRandom.Instance.AddEntropy(pbData);
break; break;
// Obsolete; for backward compatibility only
case KdbxHeaderFieldID.TransformSeed: case KdbxHeaderFieldID.TransformSeed:
m_pbTransformSeed = pbData; Debug.Assert(m_uFileVersion < FileVersion32_4);
AesKdf kdfS = new AesKdf();
if(!m_pwDatabase.KdfParameters.KdfUuid.Equals(kdfS.Uuid))
m_pwDatabase.KdfParameters = kdfS.GetDefaultParameters();
// m_pbTransformSeed = pbData;
m_pwDatabase.KdfParameters.SetByteArray(AesKdf.ParamSeed, pbData);
CryptoRandom.Instance.AddEntropy(pbData); CryptoRandom.Instance.AddEntropy(pbData);
break; break;
// Obsolete; for backward compatibility only
case KdbxHeaderFieldID.TransformRounds: case KdbxHeaderFieldID.TransformRounds:
m_pwDatabase.KeyEncryptionRounds = MemUtil.BytesToUInt64(pbData); Debug.Assert(m_uFileVersion < FileVersion32_4);
AesKdf kdfR = new AesKdf();
if(!m_pwDatabase.KdfParameters.KdfUuid.Equals(kdfR.Uuid))
m_pwDatabase.KdfParameters = kdfR.GetDefaultParameters();
// m_pwDatabase.KeyEncryptionRounds = MemUtil.BytesToUInt64(pbData);
m_pwDatabase.KdfParameters.SetUInt64(AesKdf.ParamRounds,
MemUtil.BytesToUInt64(pbData));
break; break;
case KdbxHeaderFieldID.EncryptionIV: case KdbxHeaderFieldID.EncryptionIV:
m_pbEncryptionIV = pbData; m_pbEncryptionIV = pbData;
break; break;
case KdbxHeaderFieldID.ProtectedStreamKey: case KdbxHeaderFieldID.InnerRandomStreamKey:
m_pbProtectedStreamKey = pbData; Debug.Assert(m_uFileVersion < FileVersion32_4);
Debug.Assert(m_pbInnerRandomStreamKey == null);
m_pbInnerRandomStreamKey = pbData;
CryptoRandom.Instance.AddEntropy(pbData); CryptoRandom.Instance.AddEntropy(pbData);
break; break;
case KdbxHeaderFieldID.StreamStartBytes: case KdbxHeaderFieldID.StreamStartBytes:
Debug.Assert(m_uFileVersion < FileVersion32_4);
m_pbStreamStartBytes = pbData; m_pbStreamStartBytes = pbData;
break; break;
case KdbxHeaderFieldID.InnerRandomStreamID: case KdbxHeaderFieldID.InnerRandomStreamID:
Debug.Assert(m_uFileVersion < FileVersion32_4);
SetInnerRandomStreamID(pbData); SetInnerRandomStreamID(pbData);
break; break;
case KdbxHeaderFieldID.KdfParameters:
m_pwDatabase.KdfParameters = KdfParameters.DeserializeExt(pbData);
break;
case KdbxHeaderFieldID.PublicCustomData:
Debug.Assert(m_pwDatabase.PublicCustomData.Count == 0);
m_pwDatabase.PublicCustomData = VariantDictionary.Deserialize(pbData);
break;
default: default:
Debug.Assert(false); Debug.Assert(false);
if(m_slLogger != null) if(m_slLogger != null)
@@ -311,9 +412,71 @@ namespace ModernKeePassLib.Serialization
return bResult; return bResult;
} }
private void LoadInnerHeader(Stream s)
{
BinaryReaderEx br = new BinaryReaderEx(s, StrUtil.Utf8,
KLRes.FileCorrupted + " " + KLRes.FileIncompleteExpc);
while(true)
{
if(!ReadInnerHeaderField(br)) break;
}
}
private bool ReadInnerHeaderField(BinaryReaderEx br)
{
Debug.Assert(br != null);
if(br == null) throw new ArgumentNullException("br");
byte btFieldID = br.ReadByte();
int cbSize = MemUtil.BytesToInt32(br.ReadBytes(4));
if(cbSize < 0) throw new FormatException(KLRes.FileCorrupted);
byte[] pbData = MemUtil.EmptyByteArray;
if(cbSize > 0) pbData = br.ReadBytes(cbSize);
bool bResult = true;
KdbxInnerHeaderFieldID kdbID = (KdbxInnerHeaderFieldID)btFieldID;
switch(kdbID)
{
case KdbxInnerHeaderFieldID.EndOfHeader:
bResult = false; // Returning false indicates end of header
break;
case KdbxInnerHeaderFieldID.InnerRandomStreamID:
SetInnerRandomStreamID(pbData);
break;
case KdbxInnerHeaderFieldID.InnerRandomStreamKey:
Debug.Assert(m_pbInnerRandomStreamKey == null);
m_pbInnerRandomStreamKey = pbData;
CryptoRandom.Instance.AddEntropy(pbData);
break;
case KdbxInnerHeaderFieldID.Binary:
if(pbData.Length < 1) throw new FormatException();
KdbxBinaryFlags f = (KdbxBinaryFlags)pbData[0];
bool bProt = ((f & KdbxBinaryFlags.Protected) != KdbxBinaryFlags.None);
ProtectedBinary pb = new ProtectedBinary(bProt, pbData,
1, pbData.Length - 1);
m_pbsBinaries.Add(pb);
if(bProt) MemUtil.ZeroByteArray(pbData);
break;
default:
Debug.Assert(false);
break;
}
return bResult;
}
private void SetCipher(byte[] pbID) private void SetCipher(byte[] pbID)
{ {
if((pbID == null) || (pbID.Length != 16)) if((pbID == null) || (pbID.Length != (int)PwUuid.UuidSize))
throw new FormatException(KLRes.FileUnknownCipher); throw new FormatException(KLRes.FileUnknownCipher);
m_pwDatabase.DataCipherUuid = new PwUuid(pbID); m_pwDatabase.DataCipherUuid = new PwUuid(pbID);
@@ -337,57 +500,34 @@ namespace ModernKeePassLib.Serialization
m_craInnerRandomStream = (CrsAlgorithm)uID; m_craInnerRandomStream = (CrsAlgorithm)uID;
} }
private Stream AttachStreamDecryptor(Stream s) [Obsolete]
public static List<PwEntry> ReadEntries(Stream msData)
{ {
MemoryStream ms = new MemoryStream(); return ReadEntries(msData, null, false);
Debug.Assert(m_pbMasterSeed.Length == 32);
if(m_pbMasterSeed.Length != 32)
throw new FormatException(KLRes.MasterSeedLengthInvalid);
ms.Write(m_pbMasterSeed, 0, 32);
byte[] pKey32 = m_pwDatabase.MasterKey.GenerateKey32(m_pbTransformSeed,
m_pwDatabase.KeyEncryptionRounds).ReadData();
if((pKey32 == null) || (pKey32.Length != 32))
throw new SecurityException(KLRes.InvalidCompositeKey);
ms.Write(pKey32, 0, 32);
#if ModernKeePassLib
/*var sha256 = WinRTCrypto.HashAlgorithmProvider.OpenAlgorithm(HashAlgorithm.Sha256);
var aesKey = sha256.HashData(ms.ToArray());*/
var sha256 = HashAlgorithmProvider.OpenAlgorithm(HashAlgorithmNames.Sha256);
var buffer = sha256.HashData(CryptographicBuffer.CreateFromByteArray(ms.ToArray()));
byte[] aesKey;
CryptographicBuffer.CopyToByteArray(buffer, out aesKey);
#else
SHA256Managed sha256 = new SHA256Managed();
byte[] aesKey = sha256.ComputeHash(ms.ToArray());
#endif
ms.Dispose();
Array.Clear(pKey32, 0, 32);
if((aesKey == null) || (aesKey.Length != 32))
throw new SecurityException(KLRes.FinalKeyCreationFailed);
ICipherEngine iEngine = CipherPool.GlobalPool.GetCipher(m_pwDatabase.DataCipherUuid);
if(iEngine == null) throw new SecurityException(KLRes.FileUnknownCipher);
return iEngine.DecryptStream(s, aesKey, m_pbEncryptionIV);
} }
[Obsolete] [Obsolete]
public static List<PwEntry> ReadEntries(PwDatabase pwDatabase, Stream msData) public static List<PwEntry> ReadEntries(PwDatabase pdContext, Stream msData)
{ {
return ReadEntries(msData); return ReadEntries(msData, pdContext, true);
} }
/// <summary> /// <summary>
/// Read entries from a stream. /// Read entries from a stream.
/// </summary> /// </summary>
/// <param name="msData">Input stream to read the entries from.</param> /// <param name="msData">Input stream to read the entries from.</param>
/// <returns>Extracted entries.</returns> /// <param name="pdContext">Context database (e.g. for storing icons).</param>
public static List<PwEntry> ReadEntries(Stream msData) /// <param name="bCopyIcons">If <c>true</c>, custom icons required by
/// the loaded entries are copied to the context database.</param>
/// <returns>Loaded entries.</returns>
public static List<PwEntry> ReadEntries(Stream msData, PwDatabase pdContext,
bool bCopyIcons)
{ {
List<PwEntry> lEntries = new List<PwEntry>();
if(msData == null) { Debug.Assert(false); return lEntries; }
// pdContext may be null
/* KdbxFile f = new KdbxFile(pwDatabase); /* KdbxFile f = new KdbxFile(pwDatabase);
f.m_format = KdbxFormat.PlainXml; f.m_format = KdbxFormat.PlainXml;
@@ -417,17 +557,37 @@ namespace ModernKeePassLib.Serialization
return vEntries; */ return vEntries; */
PwDatabase pd = new PwDatabase(); PwDatabase pd = new PwDatabase();
pd.New(new IOConnectionInfo(), new CompositeKey());
KdbxFile f = new KdbxFile(pd); KdbxFile f = new KdbxFile(pd);
f.Load(msData, KdbxFormat.PlainXml, null); f.Load(msData, KdbxFormat.PlainXml, null);
List<PwEntry> vEntries = new List<PwEntry>();
foreach(PwEntry pe in pd.RootGroup.Entries) foreach(PwEntry pe in pd.RootGroup.Entries)
{ {
pe.SetUuid(new PwUuid(true), true); pe.SetUuid(new PwUuid(true), true);
vEntries.Add(pe); lEntries.Add(pe);
if(bCopyIcons && (pdContext != null))
{
PwUuid pu = pe.CustomIconUuid;
if(!pu.Equals(PwUuid.Zero))
{
int iSrc = pd.GetCustomIconIndex(pu);
int iDst = pdContext.GetCustomIconIndex(pu);
if(iSrc < 0) { Debug.Assert(false); }
else if(iDst < 0)
{
pdContext.CustomIcons.Add(pd.CustomIcons[iSrc]);
pdContext.Modified = true;
pdContext.UINeedsIconUpdate = true;
}
}
}
} }
return vEntries; return lEntries;
} }
} }
} }

View File

@@ -49,6 +49,7 @@ using ModernKeePassLib.Security;
using ModernKeePassLib.Utility; using ModernKeePassLib.Utility;
using Windows.Security.Cryptography.Core; using Windows.Security.Cryptography.Core;
using Windows.Storage.Streams; using Windows.Storage.Streams;
using ModernKeePassLib.Cryptography.KeyDerivation;
namespace ModernKeePassLib.Serialization namespace ModernKeePassLib.Serialization
{ {
@@ -57,7 +58,7 @@ namespace ModernKeePassLib.Serialization
/// </summary> /// </summary>
public sealed partial class KdbxFile public sealed partial class KdbxFile
{ {
// public void Save(string strFile, PwGroup pgDataSource, KdbxFormat format, // public void Save(string strFile, PwGroup pgDataSource, KdbxFormat fmt,
// IStatusLogger slLogger) // IStatusLogger slLogger)
// { // {
// bool bMadeUnhidden = UrlUtil.UnhideFile(strFile); // bool bMadeUnhidden = UrlUtil.UnhideFile(strFile);
@@ -75,212 +76,324 @@ namespace ModernKeePassLib.Serialization
/// <param name="pgDataSource">Group containing all groups and /// <param name="pgDataSource">Group containing all groups and
/// entries to write. If <c>null</c>, the complete database will /// entries to write. If <c>null</c>, the complete database will
/// be written.</param> /// be written.</param>
/// <param name="format">Format of the file to create.</param> /// <param name="fmt">Format of the file to create.</param>
/// <param name="slLogger">Logger that recieves status information.</param> /// <param name="slLogger">Logger that recieves status information.</param>
public void Save(Stream sSaveTo, PwGroup pgDataSource, KdbxFormat format, public void Save(Stream sSaveTo, PwGroup pgDataSource, KdbxFormat fmt,
IStatusLogger slLogger) IStatusLogger slLogger)
{ {
Debug.Assert(sSaveTo != null); Debug.Assert(sSaveTo != null);
if(sSaveTo == null) throw new ArgumentNullException("sSaveTo"); if(sSaveTo == null) throw new ArgumentNullException("sSaveTo");
m_format = format; if(m_bUsedOnce)
throw new InvalidOperationException("Do not reuse KdbxFile objects!");
m_bUsedOnce = true;
m_format = fmt;
m_slLogger = slLogger; m_slLogger = slLogger;
HashingStreamEx hashedStream = new HashingStreamEx(sSaveTo, true, null); PwGroup pgRoot = (pgDataSource ?? m_pwDatabase.RootGroup);
UTF8Encoding encNoBom = StrUtil.Utf8; UTF8Encoding encNoBom = StrUtil.Utf8;
CryptoRandom cr = CryptoRandom.Instance; 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 try
{ {
m_uFileVersion = GetMinKdbxVersion();
int cbEncKey, cbEncIV;
ICipherEngine iCipher = GetCipher(out cbEncKey, out cbEncIV);
m_pbMasterSeed = cr.GetRandomBytes(32); m_pbMasterSeed = cr.GetRandomBytes(32);
m_pbTransformSeed = cr.GetRandomBytes(32); m_pbEncryptionIV = cr.GetRandomBytes((uint)cbEncIV);
m_pbEncryptionIV = cr.GetRandomBytes(16);
m_pbProtectedStreamKey = cr.GetRandomBytes(32); // m_pbTransformSeed = cr.GetRandomBytes(32);
m_craInnerRandomStream = CrsAlgorithm.Salsa20; PwUuid puKdf = m_pwDatabase.KdfParameters.KdfUuid;
m_randomStream = new CryptoRandomStream(m_craInnerRandomStream, KdfEngine kdf = KdfPool.Get(puKdf);
m_pbProtectedStreamKey); if(kdf == null)
throw new Exception(KLRes.UnknownKdf + Environment.NewLine +
// KLRes.FileNewVerOrPlgReq + Environment.NewLine +
"UUID: " + puKdf.ToHexString() + ".");
kdf.Randomize(m_pwDatabase.KdfParameters);
m_pbStreamStartBytes = cr.GetRandomBytes(32);
Stream writerStream;
if(m_format == KdbxFormat.Default) if(m_format == KdbxFormat.Default)
{ {
WriteHeader(hashedStream); // Also flushes the stream 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);
}
Stream sEncrypted = AttachStreamEncryptor(hashedStream); m_randomStream = new CryptoRandomStream(m_craInnerRandomStream,
if((sEncrypted == null) || (sEncrypted == hashedStream)) m_pbInnerRandomStreamKey);
throw new SecurityException(KLRes.CryptoStreamFailed); }
sEncrypted.Write(m_pbStreamStartBytes, 0, m_pbStreamStartBytes.Length); if(m_uFileVersion < FileVersion32_4)
m_pbStreamStartBytes = cr.GetRandomBytes(32);
Stream sHashed = new HashedBlockStream(sEncrypted, true); 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) if(m_pwDatabase.Compression == PwCompressionAlgorithm.GZip)
writerStream = new GZipStream(sHashed, CompressionMode.Compress); {
else sXml = new GZipStream(sPlain, CompressionMode.Compress);
writerStream = sHashed; lStreams.Add(sXml);
}
else sXml = sPlain;
if(m_uFileVersion >= FileVersion32_4)
WriteInnerHeader(sXml); // Binary header before XML
} }
else if(m_format == KdbxFormat.PlainXml) else if(m_format == KdbxFormat.PlainXml)
writerStream = hashedStream; sXml = sHashing;
else { Debug.Assert(false); throw new FormatException("KdbFormat"); } else
{
Debug.Assert(false);
throw new ArgumentOutOfRangeException("fmt");
}
#if ModernKeePassLib #if ModernKeePassLib || KeePassUAP
var settings = new XmlWriterSettings() { XmlWriterSettings xws = new XmlWriterSettings();
Encoding = encNoBom, xws.Encoding = encNoBom;
Indent = true, xws.Indent = true;
IndentChars = "\t", xws.IndentChars = "\t";
NewLineChars = "\r\n", xws.NewLineOnAttributes = false;
};
m_xmlWriter = XmlWriter.Create(writerStream, settings); XmlWriter xw = XmlWriter.Create(sXml, xws);
#else #else
m_xmlWriter = new XmlTextWriter(writerStream, encNoBom); XmlTextWriter xw = new XmlTextWriter(sXml, encNoBom);
xw.Formatting = Formatting.Indented;
xw.IndentChar = '\t';
xw.Indentation = 1;
#endif #endif
WriteDocument(pgDataSource); m_xmlWriter = xw;
WriteDocument(pgRoot);
m_xmlWriter.Flush(); m_xmlWriter.Flush();
m_xmlWriter.Dispose(); m_xmlWriter.Dispose();
writerStream.Dispose();
} }
finally { CommonCleanUpWrite(sSaveTo, hashedStream); } finally
{
if(pbCipherKey != null) MemUtil.ZeroByteArray(pbCipherKey);
if(pbHmacKey64 != null) MemUtil.ZeroByteArray(pbHmacKey64);
CommonCleanUpWrite(lStreams, sHashing);
}
} }
private void CommonCleanUpWrite(Stream sSaveTo, HashingStreamEx hashedStream) private void CommonCleanUpWrite(List<Stream> lStreams, HashingStreamEx sHashing)
{ {
hashedStream.Dispose(); CloseStreams(lStreams);
m_pbHashOfFileOnDisk = hashedStream.Hash;
sSaveTo.Dispose(); Debug.Assert(lStreams.Contains(sHashing)); // sHashing must be closed
m_pbHashOfFileOnDisk = sHashing.Hash;
Debug.Assert(m_pbHashOfFileOnDisk != null);
CleanUpInnerRandomStream();
m_xmlWriter = null; m_xmlWriter = null;
m_pbHashOfHeader = null; m_pbHashOfHeader = null;
} }
private void WriteHeader(Stream s) private byte[] GenerateHeader()
{ {
using (var ms = new MemoryStream()) byte[] pbHeader;
{ using(MemoryStream ms = new MemoryStream())
{
MemUtil.Write(ms, MemUtil.UInt32ToBytes(FileSignature1));
MemUtil.Write(ms, MemUtil.UInt32ToBytes(FileSignature2));
MemUtil.Write(ms, MemUtil.UInt32ToBytes(m_uFileVersion));
MemUtil.Write(ms, MemUtil.UInt32ToBytes(FileSignature1)); WriteHeaderField(ms, KdbxHeaderFieldID.CipherID,
MemUtil.Write(ms, MemUtil.UInt32ToBytes(FileSignature2)); m_pwDatabase.DataCipherUuid.UuidBytes);
MemUtil.Write(ms, MemUtil.UInt32ToBytes(FileVersion32));
WriteHeaderField(ms, KdbxHeaderFieldID.CipherID, int nCprID = (int)m_pwDatabase.Compression;
m_pwDatabase.DataCipherUuid.UuidBytes); WriteHeaderField(ms, KdbxHeaderFieldID.CompressionFlags,
MemUtil.UInt32ToBytes((uint)nCprID));
int nCprID = (int) m_pwDatabase.Compression; WriteHeaderField(ms, KdbxHeaderFieldID.MasterSeed, m_pbMasterSeed);
WriteHeaderField(ms, KdbxHeaderFieldID.CompressionFlags,
MemUtil.UInt32ToBytes((uint) nCprID));
WriteHeaderField(ms, KdbxHeaderFieldID.MasterSeed, m_pbMasterSeed); if(m_uFileVersion < FileVersion32_4)
WriteHeaderField(ms, KdbxHeaderFieldID.TransformSeed, m_pbTransformSeed); {
WriteHeaderField(ms, KdbxHeaderFieldID.TransformRounds, Debug.Assert(m_pwDatabase.KdfParameters.KdfUuid.Equals(
MemUtil.UInt64ToBytes(m_pwDatabase.KeyEncryptionRounds)); (new AesKdf()).Uuid));
WriteHeaderField(ms, KdbxHeaderFieldID.EncryptionIV, m_pbEncryptionIV); WriteHeaderField(ms, KdbxHeaderFieldID.TransformSeed,
WriteHeaderField(ms, KdbxHeaderFieldID.ProtectedStreamKey, m_pbProtectedStreamKey); m_pwDatabase.KdfParameters.GetByteArray(AesKdf.ParamSeed));
WriteHeaderField(ms, KdbxHeaderFieldID.StreamStartBytes, m_pbStreamStartBytes); WriteHeaderField(ms, KdbxHeaderFieldID.TransformRounds,
MemUtil.UInt64ToBytes(m_pwDatabase.KdfParameters.GetUInt64(
AesKdf.ParamRounds, PwDefs.DefaultKeyEncryptionRounds)));
}
else
WriteHeaderField(ms, KdbxHeaderFieldID.KdfParameters,
KdfParameters.SerializeExt(m_pwDatabase.KdfParameters));
int nIrsID = (int) m_craInnerRandomStream; if(m_pbEncryptionIV.Length > 0)
WriteHeaderField(ms, KdbxHeaderFieldID.InnerRandomStreamID, WriteHeaderField(ms, KdbxHeaderFieldID.EncryptionIV, m_pbEncryptionIV);
MemUtil.UInt32ToBytes((uint) nIrsID));
WriteHeaderField(ms, KdbxHeaderFieldID.EndOfHeader, new byte[] if(m_uFileVersion < FileVersion32_4)
{ {
(byte) '\r', (byte) '\n', (byte) '\r', (byte) '\n' WriteHeaderField(ms, KdbxHeaderFieldID.InnerRandomStreamKey,
}); m_pbInnerRandomStreamKey);
byte[] pbHeader = ms.ToArray(); WriteHeaderField(ms, KdbxHeaderFieldID.StreamStartBytes,
m_pbStreamStartBytes);
#if ModernKeePassLib int nIrsID = (int)m_craInnerRandomStream;
/*var sha256 = WinRTCrypto.HashAlgorithmProvider.OpenAlgorithm(HashAlgorithm.Sha256); WriteHeaderField(ms, KdbxHeaderFieldID.InnerRandomStreamID,
m_pbHashOfHeader = sha256.HashData(pbHeader);*/ MemUtil.Int32ToBytes(nIrsID));
var sha256 = HashAlgorithmProvider.OpenAlgorithm(HashAlgorithmNames.Sha256); }
var buffer = sha256.HashData(CryptographicBuffer.CreateFromByteArray(pbHeader));
CryptographicBuffer.CopyToByteArray(buffer, out m_pbHashOfHeader);
#else
SHA256Managed sha256 = new SHA256Managed();
m_pbHashOfHeader = sha256.ComputeHash(pbHeader);
#endif
s.Write(pbHeader, 0, pbHeader.Length); // Write public custom data only when there is at least one item,
s.Flush(); // because KDBX 3.1 didn't support this field yet
} if(m_pwDatabase.PublicCustomData.Count > 0)
WriteHeaderField(ms, KdbxHeaderFieldID.PublicCustomData,
VariantDictionary.Serialize(m_pwDatabase.PublicCustomData));
WriteHeaderField(ms, KdbxHeaderFieldID.EndOfHeader, new byte[] {
(byte)'\r', (byte)'\n', (byte)'\r', (byte)'\n' });
pbHeader = ms.ToArray();
}
return pbHeader;
} }
private static void WriteHeaderField(Stream s, KdbxHeaderFieldID kdbID, private void WriteHeaderField(Stream s, KdbxHeaderFieldID kdbID,
byte[] pbData) byte[] pbData)
{ {
s.WriteByte((byte)kdbID); s.WriteByte((byte)kdbID);
if(pbData != null) byte[] pb = (pbData ?? MemUtil.EmptyByteArray);
int cb = pb.Length;
if(cb < 0) { Debug.Assert(false); throw new OutOfMemoryException(); }
Debug.Assert(m_uFileVersion > 0);
if(m_uFileVersion < FileVersion32_4)
{ {
ushort uLength = (ushort)pbData.Length; if(cb > (int)ushort.MaxValue)
MemUtil.Write(s, MemUtil.UInt16ToBytes(uLength)); {
Debug.Assert(false);
throw new ArgumentOutOfRangeException("pbData");
}
if(uLength > 0) s.Write(pbData, 0, pbData.Length); MemUtil.Write(s, MemUtil.UInt16ToBytes((ushort)cb));
} }
else MemUtil.Write(s, MemUtil.UInt16ToBytes((ushort)0)); else MemUtil.Write(s, MemUtil.Int32ToBytes(cb));
MemUtil.Write(s, pb);
} }
private Stream AttachStreamEncryptor(Stream s) private void WriteInnerHeader(Stream s)
{ {
using (var ms = new MemoryStream()) int nIrsID = (int)m_craInnerRandomStream;
{ WriteInnerHeaderField(s, KdbxInnerHeaderFieldID.InnerRandomStreamID,
Debug.Assert(m_pbMasterSeed != null); MemUtil.Int32ToBytes(nIrsID), null);
Debug.Assert(m_pbMasterSeed.Length == 32);
ms.Write(m_pbMasterSeed, 0, 32);
Debug.Assert(m_pwDatabase != null); WriteInnerHeaderField(s, KdbxInnerHeaderFieldID.InnerRandomStreamKey,
Debug.Assert(m_pwDatabase.MasterKey != null); m_pbInnerRandomStreamKey, null);
ProtectedBinary pbinKey = m_pwDatabase.MasterKey.GenerateKey32(
m_pbTransformSeed, m_pwDatabase.KeyEncryptionRounds);
Debug.Assert(pbinKey != null);
if (pbinKey == null)
throw new SecurityException(KLRes.InvalidCompositeKey);
byte[] pKey32 = pbinKey.ReadData();
if ((pKey32 == null) || (pKey32.Length != 32))
throw new SecurityException(KLRes.InvalidCompositeKey);
ms.Write(pKey32, 0, 32);
#if ModernKeePassLib ProtectedBinary[] vBin = m_pbsBinaries.ToArray();
/*var sha256 = WinRTCrypto.HashAlgorithmProvider.OpenAlgorithm(HashAlgorithm.Sha256); for(int i = 0; i < vBin.Length; ++i)
var aesKey = sha256.HashData(ms.ToArray());*/ {
var sha256 = HashAlgorithmProvider.OpenAlgorithm(HashAlgorithmNames.Sha256); ProtectedBinary pb = vBin[i];
var buffer = sha256.HashData(CryptographicBuffer.CreateFromByteArray(ms.ToArray())); if(pb == null) throw new InvalidOperationException();
byte[] aesKey;
CryptographicBuffer.CopyToByteArray(buffer, out aesKey);
#else
SHA256Managed sha256 = new SHA256Managed();
byte[] aesKey = sha256.ComputeHash(ms.ToArray());
#endif
Array.Clear(pKey32, 0, 32);
Debug.Assert(CipherPool.GlobalPool != null); KdbxBinaryFlags f = KdbxBinaryFlags.None;
ICipherEngine iEngine = CipherPool.GlobalPool.GetCipher(m_pwDatabase.DataCipherUuid); if(pb.IsProtected) f |= KdbxBinaryFlags.Protected;
if (iEngine == null) throw new SecurityException(KLRes.FileUnknownCipher);
return iEngine.EncryptStream(s, aesKey, m_pbEncryptionIV); byte[] pbFlags = new byte[1] { (byte)f };
} byte[] pbData = pb.ReadData();
WriteInnerHeaderField(s, KdbxInnerHeaderFieldID.Binary,
pbFlags, pbData);
if(pb.IsProtected) MemUtil.ZeroByteArray(pbData);
}
WriteInnerHeaderField(s, KdbxInnerHeaderFieldID.EndOfHeader,
null, null);
} }
private void WriteDocument(PwGroup pgDataSource) private void WriteInnerHeaderField(Stream s, KdbxInnerHeaderFieldID kdbID,
byte[] pbData1, byte[] pbData2)
{
s.WriteByte((byte)kdbID);
byte[] pb1 = (pbData1 ?? MemUtil.EmptyByteArray);
byte[] pb2 = (pbData2 ?? MemUtil.EmptyByteArray);
int cb = pb1.Length + pb2.Length;
if(cb < 0) { Debug.Assert(false); throw new OutOfMemoryException(); }
MemUtil.Write(s, MemUtil.Int32ToBytes(cb));
MemUtil.Write(s, pb1);
MemUtil.Write(s, pb2);
}
private void WriteDocument(PwGroup pgRoot)
{ {
Debug.Assert(m_xmlWriter != null); Debug.Assert(m_xmlWriter != null);
if(m_xmlWriter == null) throw new InvalidOperationException(); if(m_xmlWriter == null) throw new InvalidOperationException();
PwGroup pgRoot = (pgDataSource ?? m_pwDatabase.RootGroup);
uint uNumGroups, uNumEntries, uCurEntry = 0; uint uNumGroups, uNumEntries, uCurEntry = 0;
pgRoot.GetCounts(true, out uNumGroups, out uNumEntries); pgRoot.GetCounts(true, out uNumGroups, out uNumEntries);
BinPoolBuild(pgRoot);
#if !ModernKeePassLib
m_xmlWriter.Formatting = Formatting.Indented;
m_xmlWriter.IndentChar = '\t';
m_xmlWriter.Indentation = 1;
#endif
m_xmlWriter.WriteStartDocument(true); m_xmlWriter.WriteStartDocument(true);
m_xmlWriter.WriteStartElement(ElemDocNode); m_xmlWriter.WriteStartElement(ElemDocNode);
@@ -352,12 +465,15 @@ namespace ModernKeePassLib.Serialization
{ {
m_xmlWriter.WriteStartElement(ElemMeta); m_xmlWriter.WriteStartElement(ElemMeta);
WriteObject(ElemGenerator, PwDatabase.LocalizedAppName, false); // Generator name WriteObject(ElemGenerator, PwDatabase.LocalizedAppName, false);
if(m_pbHashOfHeader != null) if((m_pbHashOfHeader != null) && (m_uFileVersion < FileVersion32_4))
WriteObject(ElemHeaderHash, Convert.ToBase64String( WriteObject(ElemHeaderHash, Convert.ToBase64String(
m_pbHashOfHeader), false); m_pbHashOfHeader), false);
if(m_uFileVersion >= FileVersion32_4)
WriteObject(ElemSettingsChanged, m_pwDatabase.SettingsChanged);
WriteObject(ElemDbName, m_pwDatabase.Name, true); WriteObject(ElemDbName, m_pwDatabase.Name, true);
WriteObject(ElemDbNameChanged, m_pwDatabase.NameChanged); WriteObject(ElemDbNameChanged, m_pwDatabase.NameChanged);
WriteObject(ElemDbDesc, m_pwDatabase.Description, true); WriteObject(ElemDbDesc, m_pwDatabase.Description, true);
@@ -369,6 +485,8 @@ namespace ModernKeePassLib.Serialization
WriteObject(ElemDbKeyChanged, m_pwDatabase.MasterKeyChanged); WriteObject(ElemDbKeyChanged, m_pwDatabase.MasterKeyChanged);
WriteObject(ElemDbKeyChangeRec, m_pwDatabase.MasterKeyChangeRec); WriteObject(ElemDbKeyChangeRec, m_pwDatabase.MasterKeyChangeRec);
WriteObject(ElemDbKeyChangeForce, m_pwDatabase.MasterKeyChangeForce); WriteObject(ElemDbKeyChangeForce, m_pwDatabase.MasterKeyChangeForce);
if(m_pwDatabase.MasterKeyChangeForceOnce)
WriteObject(ElemDbKeyChangeForceOnce, true);
WriteList(ElemMemoryProt, m_pwDatabase.MemoryProtection); WriteList(ElemMemoryProt, m_pwDatabase.MemoryProtection);
@@ -385,7 +503,9 @@ namespace ModernKeePassLib.Serialization
WriteObject(ElemLastSelectedGroup, m_pwDatabase.LastSelectedGroup); WriteObject(ElemLastSelectedGroup, m_pwDatabase.LastSelectedGroup);
WriteObject(ElemLastTopVisibleGroup, m_pwDatabase.LastTopVisibleGroup); WriteObject(ElemLastTopVisibleGroup, m_pwDatabase.LastTopVisibleGroup);
WriteBinPool(); if((m_format != KdbxFormat.Default) || (m_uFileVersion < FileVersion32_4))
WriteBinPool();
WriteList(ElemCustomData, m_pwDatabase.CustomData); WriteList(ElemCustomData, m_pwDatabase.CustomData);
m_xmlWriter.WriteEndElement(); m_xmlWriter.WriteEndElement();
@@ -408,6 +528,9 @@ namespace ModernKeePassLib.Serialization
WriteObject(ElemEnableAutoType, StrUtil.BoolToStringEx(pg.EnableAutoType), false); WriteObject(ElemEnableAutoType, StrUtil.BoolToStringEx(pg.EnableAutoType), false);
WriteObject(ElemEnableSearching, StrUtil.BoolToStringEx(pg.EnableSearching), false); WriteObject(ElemEnableSearching, StrUtil.BoolToStringEx(pg.EnableSearching), false);
WriteObject(ElemLastTopVisibleEntry, pg.LastTopVisibleEntry); WriteObject(ElemLastTopVisibleEntry, pg.LastTopVisibleEntry);
if(pg.CustomData.Count > 0)
WriteList(ElemCustomData, pg.CustomData);
} }
private void EndGroup() private void EndGroup()
@@ -438,6 +561,9 @@ namespace ModernKeePassLib.Serialization
WriteList(pe.Binaries); WriteList(pe.Binaries);
WriteList(ElemAutoType, pe.AutoType); WriteList(ElemAutoType, pe.AutoType);
if(pe.CustomData.Count > 0)
WriteList(ElemCustomData, pe.CustomData);
if(!bIsHistory) WriteList(ElemHistory, pe.History, true); if(!bIsHistory) WriteList(ElemHistory, pe.History, true);
else { Debug.Assert(pe.History.UCount == 0); } else { Debug.Assert(pe.History.UCount == 0); }
@@ -647,8 +773,28 @@ namespace ModernKeePassLib.Serialization
private void WriteObject(string name, DateTime value) private void WriteObject(string name, DateTime value)
{ {
Debug.Assert(name != null); Debug.Assert(name != null);
Debug.Assert(value.Kind == DateTimeKind.Utc);
WriteObject(name, TimeUtil.SerializeUtc(value), false); // Cf. ReadTime
if((m_format == KdbxFormat.Default) && (m_uFileVersion >= FileVersion32_4))
{
DateTime dt = TimeUtil.ToUtc(value, false);
// DateTime dtBase = new DateTime(2000, 1, 1, 0, 0, 0, DateTimeKind.Utc);
// dt -= new TimeSpan(dtBase.Ticks);
// WriteObject(name, dt.ToBinary());
// dt = TimeUtil.RoundToMultOf2PowLess1s(dt);
// long lBin = dt.ToBinary();
long lSec = dt.Ticks / TimeSpan.TicksPerSecond;
// WriteObject(name, lSec);
byte[] pb = MemUtil.Int64ToBytes(lSec);
WriteObject(name, Convert.ToBase64String(pb), false);
}
else WriteObject(name, TimeUtil.SerializeUtc(value), false);
} }
private void WriteObject(string name, string strKeyName, private void WriteObject(string name, string strKeyName,
@@ -695,7 +841,7 @@ namespace ModernKeePassLib.Serialization
bProtected = m_pwDatabase.MemoryProtection.ProtectNotes; bProtected = m_pwDatabase.MemoryProtection.ProtectNotes;
} }
if(bProtected && (m_format != KdbxFormat.PlainXml)) if(bProtected && (m_format == KdbxFormat.Default))
{ {
m_xmlWriter.WriteAttributeString(AttrProtected, ValTrue); m_xmlWriter.WriteAttributeString(AttrProtected, ValTrue);
@@ -770,11 +916,15 @@ namespace ModernKeePassLib.Serialization
m_xmlWriter.WriteEndElement(); m_xmlWriter.WriteEndElement();
m_xmlWriter.WriteStartElement(ElemValue); m_xmlWriter.WriteStartElement(ElemValue);
string strRef = (bAllowRef ? BinPoolFind(value) : null); string strRef = null;
if(strRef != null) if(bAllowRef)
{ {
m_xmlWriter.WriteAttributeString(AttrRef, strRef); int iRef = m_pbsBinaries.Find(value);
if(iRef >= 0) strRef = iRef.ToString(NumberFormatInfo.InvariantInfo);
else { Debug.Assert(false); }
} }
if(strRef != null)
m_xmlWriter.WriteAttributeString(AttrRef, strRef);
else SubWriteValue(value); else SubWriteValue(value);
m_xmlWriter.WriteEndElement(); // ElemValue m_xmlWriter.WriteEndElement(); // ElemValue
@@ -783,7 +933,7 @@ namespace ModernKeePassLib.Serialization
private void SubWriteValue(ProtectedBinary value) private void SubWriteValue(ProtectedBinary value)
{ {
if(value.IsProtected && (m_format != KdbxFormat.PlainXml)) if(value.IsProtected && (m_format == KdbxFormat.Default))
{ {
m_xmlWriter.WriteAttributeString(AttrProtected, ValTrue); m_xmlWriter.WriteAttributeString(AttrProtected, ValTrue);
@@ -793,18 +943,26 @@ namespace ModernKeePassLib.Serialization
} }
else else
{ {
if(m_pwDatabase.Compression == PwCompressionAlgorithm.GZip) if(m_pwDatabase.Compression != PwCompressionAlgorithm.None)
{ {
m_xmlWriter.WriteAttributeString(AttrCompressed, ValTrue); m_xmlWriter.WriteAttributeString(AttrCompressed, ValTrue);
byte[] pbRaw = value.ReadData(); byte[] pbRaw = value.ReadData();
byte[] pbCmp = MemUtil.Compress(pbRaw); byte[] pbCmp = MemUtil.Compress(pbRaw);
m_xmlWriter.WriteBase64(pbCmp, 0, pbCmp.Length); m_xmlWriter.WriteBase64(pbCmp, 0, pbCmp.Length);
if(value.IsProtected)
{
MemUtil.ZeroByteArray(pbRaw);
MemUtil.ZeroByteArray(pbCmp);
}
} }
else else
{ {
byte[] pbRaw = value.ReadData(); byte[] pbRaw = value.ReadData();
m_xmlWriter.WriteBase64(pbRaw, 0, pbRaw.Length); m_xmlWriter.WriteBase64(pbRaw, 0, pbRaw.Length);
if(value.IsProtected) MemUtil.ZeroByteArray(pbRaw);
} }
} }
} }
@@ -824,11 +982,13 @@ namespace ModernKeePassLib.Serialization
{ {
m_xmlWriter.WriteStartElement(ElemBinaries); m_xmlWriter.WriteStartElement(ElemBinaries);
foreach(KeyValuePair<string, ProtectedBinary> kvp in m_dictBinPool) ProtectedBinary[] v = m_pbsBinaries.ToArray();
for(int i = 0; i < v.Length; ++i)
{ {
m_xmlWriter.WriteStartElement(ElemBinary); m_xmlWriter.WriteStartElement(ElemBinary);
m_xmlWriter.WriteAttributeString(AttrId, kvp.Key); m_xmlWriter.WriteAttributeString(AttrId,
SubWriteValue(kvp.Value); i.ToString(NumberFormatInfo.InvariantInfo));
SubWriteValue(v[i]);
m_xmlWriter.WriteEndElement(); m_xmlWriter.WriteEndElement();
} }
@@ -836,21 +996,18 @@ namespace ModernKeePassLib.Serialization
} }
[Obsolete] [Obsolete]
public static bool WriteEntries(Stream msOutput, PwDatabase pwDatabase,
PwEntry[] vEntries)
{
return WriteEntries(msOutput, vEntries);
}
/// <summary>
/// Write entries to a stream.
/// </summary>
/// <param name="msOutput">Output stream to which the entries will be written.</param>
/// <param name="vEntries">Entries to serialize.</param>
/// <returns>Returns <c>true</c>, if the entries were written successfully
/// to the stream.</returns>
public static bool WriteEntries(Stream msOutput, PwEntry[] vEntries) public static bool WriteEntries(Stream msOutput, PwEntry[] vEntries)
{ {
return WriteEntries(msOutput, null, vEntries);
}
public static bool WriteEntries(Stream msOutput, PwDatabase pdContext,
PwEntry[] vEntries)
{
if(msOutput == null) { Debug.Assert(false); return false; }
// pdContext may be null
if(vEntries == null) { Debug.Assert(false); return false; }
/* KdbxFile f = new KdbxFile(pwDatabase); /* KdbxFile f = new KdbxFile(pwDatabase);
f.m_format = KdbxFormat.PlainXml; f.m_format = KdbxFormat.PlainXml;
@@ -881,8 +1038,27 @@ namespace ModernKeePassLib.Serialization
PwDatabase pd = new PwDatabase(); PwDatabase pd = new PwDatabase();
pd.New(new IOConnectionInfo(), new CompositeKey()); pd.New(new IOConnectionInfo(), new CompositeKey());
foreach(PwEntry peCopy in vEntries) PwGroup pg = pd.RootGroup;
pd.RootGroup.AddEntry(peCopy.CloneDeep(), true); if(pg == null) { Debug.Assert(false); return false; }
foreach(PwEntry pe in vEntries)
{
PwUuid pu = pe.CustomIconUuid;
if(!pu.Equals(PwUuid.Zero) && (pd.GetCustomIconIndex(pu) < 0))
{
int i = -1;
if(pdContext != null) i = pdContext.GetCustomIconIndex(pu);
if(i >= 0)
{
PwCustomIcon ci = pdContext.CustomIcons[i];
pd.CustomIcons.Add(ci);
}
else { Debug.Assert(pdContext == null); }
}
PwEntry peCopy = pe.CloneDeep();
pg.AddEntry(peCopy, true);
}
KdbxFile f = new KdbxFile(pd); KdbxFile f = new KdbxFile(pd);
f.Save(msOutput, null, KdbxFormat.PlainXml, null); f.Save(msOutput, null, KdbxFormat.PlainXml, null);

View File

@@ -24,7 +24,9 @@ using System.Text;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using System.Diagnostics; using System.Diagnostics;
using System.Security;
using Windows.Security.Cryptography;
using Windows.Security.Cryptography.Core;
#if !KeePassLibSD #if !KeePassLibSD
using System.IO.Compression; using System.IO.Compression;
#endif #endif
@@ -36,8 +38,11 @@ using Windows.Storage;
using ModernKeePassLib.Collections; using ModernKeePassLib.Collections;
using ModernKeePassLib.Cryptography; using ModernKeePassLib.Cryptography;
using ModernKeePassLib.Cryptography.Cipher;
using ModernKeePassLib.Cryptography.KeyDerivation;
using ModernKeePassLib.Delegates; using ModernKeePassLib.Delegates;
using ModernKeePassLib.Interfaces; using ModernKeePassLib.Interfaces;
using ModernKeePassLib.Resources;
using ModernKeePassLib.Security; using ModernKeePassLib.Security;
using ModernKeePassLib.Utility; using ModernKeePassLib.Utility;
@@ -82,7 +87,10 @@ namespace ModernKeePassLib.Serialization
/// The first 2 bytes are critical (i.e. loading will fail, if the /// The first 2 bytes are critical (i.e. loading will fail, if the
/// file version is too high), the last 2 bytes are informational. /// file version is too high), the last 2 bytes are informational.
/// </summary> /// </summary>
private const uint FileVersion32 = 0x00030001; private const uint FileVersion32 = 0x00040000;
internal const uint FileVersion32_4 = 0x00040000; // First of 4.x series
internal const uint FileVersion32_3 = 0x00030001; // Old format 3.1
private const uint FileVersionCriticalMask = 0xFFFF0000; private const uint FileVersionCriticalMask = 0xFFFF0000;
@@ -101,6 +109,7 @@ namespace ModernKeePassLib.Serialization
private const string ElemGenerator = "Generator"; private const string ElemGenerator = "Generator";
private const string ElemHeaderHash = "HeaderHash"; private const string ElemHeaderHash = "HeaderHash";
private const string ElemSettingsChanged = "SettingsChanged";
private const string ElemDbName = "DatabaseName"; private const string ElemDbName = "DatabaseName";
private const string ElemDbNameChanged = "DatabaseNameChanged"; private const string ElemDbNameChanged = "DatabaseNameChanged";
private const string ElemDbDesc = "DatabaseDescription"; private const string ElemDbDesc = "DatabaseDescription";
@@ -112,6 +121,7 @@ namespace ModernKeePassLib.Serialization
private const string ElemDbKeyChanged = "MasterKeyChanged"; private const string ElemDbKeyChanged = "MasterKeyChanged";
private const string ElemDbKeyChangeRec = "MasterKeyChangeRec"; private const string ElemDbKeyChangeRec = "MasterKeyChangeRec";
private const string ElemDbKeyChangeForce = "MasterKeyChangeForce"; private const string ElemDbKeyChangeForce = "MasterKeyChangeForce";
private const string ElemDbKeyChangeForceOnce = "MasterKeyChangeForceOnce";
private const string ElemRecycleBinEnabled = "RecycleBinEnabled"; private const string ElemRecycleBinEnabled = "RecycleBinEnabled";
private const string ElemRecycleBinUuid = "RecycleBinUUID"; private const string ElemRecycleBinUuid = "RecycleBinUUID";
private const string ElemRecycleBinChanged = "RecycleBinChanged"; private const string ElemRecycleBinChanged = "RecycleBinChanged";
@@ -195,6 +205,7 @@ namespace ModernKeePassLib.Serialization
private const string ElemStringDictExItem = "Item"; private const string ElemStringDictExItem = "Item";
private PwDatabase m_pwDatabase; // Not null, see constructor private PwDatabase m_pwDatabase; // Not null, see constructor
private bool m_bUsedOnce = false;
#if ModernKeePassLib #if ModernKeePassLib
private XmlWriter m_xmlWriter = null; private XmlWriter m_xmlWriter = null;
@@ -205,23 +216,23 @@ namespace ModernKeePassLib.Serialization
private KdbxFormat m_format = KdbxFormat.Default; private KdbxFormat m_format = KdbxFormat.Default;
private IStatusLogger m_slLogger = null; private IStatusLogger m_slLogger = null;
private uint m_uFileVersion = 0;
private byte[] m_pbMasterSeed = null; private byte[] m_pbMasterSeed = null;
private byte[] m_pbTransformSeed = null; // private byte[] m_pbTransformSeed = null;
private byte[] m_pbEncryptionIV = null; private byte[] m_pbEncryptionIV = null;
private byte[] m_pbProtectedStreamKey = null;
private byte[] m_pbStreamStartBytes = null; private byte[] m_pbStreamStartBytes = null;
// ArcFourVariant only for compatibility; KeePass will default to a // ArcFourVariant only for backward compatibility; KeePass defaults
// different (more secure) algorithm when *writing* databases // to a more secure algorithm when *writing* databases
private CrsAlgorithm m_craInnerRandomStream = CrsAlgorithm.ArcFourVariant; private CrsAlgorithm m_craInnerRandomStream = CrsAlgorithm.ArcFourVariant;
private byte[] m_pbInnerRandomStreamKey = null;
private Dictionary<string, ProtectedBinary> m_dictBinPool = private ProtectedBinarySet m_pbsBinaries = new ProtectedBinarySet();
new Dictionary<string, ProtectedBinary>();
private byte[] m_pbHashOfHeader = null; private byte[] m_pbHashOfHeader = null;
private byte[] m_pbHashOfFileOnDisk = null; private byte[] m_pbHashOfFileOnDisk = null;
private readonly DateTime m_dtNow = DateTime.Now; // Cache current time private readonly DateTime m_dtNow = DateTime.UtcNow; // Cache current time
private const uint NeutralLanguageOffset = 0x100000; // 2^20, see 32-bit Unicode specs private const uint NeutralLanguageOffset = 0x100000; // 2^20, see 32-bit Unicode specs
private const uint NeutralLanguageIDSec = 0x7DC5C; // See 32-bit Unicode specs private const uint NeutralLanguageIDSec = 0x7DC5C; // See 32-bit Unicode specs
@@ -235,12 +246,30 @@ namespace ModernKeePassLib.Serialization
CipherID = 2, CipherID = 2,
CompressionFlags = 3, CompressionFlags = 3,
MasterSeed = 4, MasterSeed = 4,
TransformSeed = 5, TransformSeed = 5, // KDBX 3.1, for backward compatibility only
TransformRounds = 6, TransformRounds = 6, // KDBX 3.1, for backward compatibility only
EncryptionIV = 7, EncryptionIV = 7,
ProtectedStreamKey = 8, InnerRandomStreamKey = 8, // KDBX 3.1, for backward compatibility only
StreamStartBytes = 9, StreamStartBytes = 9, // KDBX 3.1, for backward compatibility only
InnerRandomStreamID = 10 InnerRandomStreamID = 10, // KDBX 3.1, for backward compatibility only
KdfParameters = 11, // KDBX 4, superseding Transform*
PublicCustomData = 12 // KDBX 4
}
// Inner header in KDBX >= 4 files
private enum KdbxInnerHeaderFieldID : byte
{
EndOfHeader = 0,
InnerRandomStreamID = 1, // Supersedes KdbxHeaderFieldID.InnerRandomStreamID
InnerRandomStreamKey = 2, // Supersedes KdbxHeaderFieldID.InnerRandomStreamKey
Binary = 3
}
[Flags]
private enum KdbxBinaryFlags : byte
{
None = 0,
Protected = 1
} }
public byte[] HashOfFileOnDisk public byte[] HashOfFileOnDisk
@@ -255,6 +284,13 @@ namespace ModernKeePassLib.Serialization
set { m_bRepairMode = value; } set { m_bRepairMode = value; }
} }
private uint m_uForceVersion = 0;
internal uint ForceVersion
{
get { return m_uForceVersion; }
set { m_uForceVersion = value; }
}
private string m_strDetachBins = null; private string m_strDetachBins = null;
/// <summary> /// <summary>
/// Detach binaries when opening a file. If this isn't <c>null</c>, /// Detach binaries when opening a file. If this isn't <c>null</c>,
@@ -299,64 +335,173 @@ namespace ModernKeePassLib.Serialization
} }
} }
private void BinPoolBuild(PwGroup pgDataSource) private uint GetMinKdbxVersion()
{ {
m_dictBinPool = new Dictionary<string, ProtectedBinary>(); if(m_uForceVersion != 0) return m_uForceVersion;
if(pgDataSource == null) { Debug.Assert(false); return; } // See also KeePassKdb2x3.Export (KDBX 3.1 export module)
EntryHandler eh = delegate(PwEntry pe) AesKdf kdfAes = new AesKdf();
if(!kdfAes.Uuid.Equals(m_pwDatabase.KdfParameters.KdfUuid))
return FileVersion32;
if(m_pwDatabase.PublicCustomData.Count > 0)
return FileVersion32;
bool bCustomData = false;
GroupHandler gh = delegate(PwGroup pg)
{ {
foreach(PwEntry peHistory in pe.History) if(pg == null) { Debug.Assert(false); return true; }
{ if(pg.CustomData.Count > 0) { bCustomData = true; return false; }
BinPoolAdd(peHistory.Binaries);
}
BinPoolAdd(pe.Binaries);
return true; return true;
}; };
EntryHandler eh = delegate(PwEntry pe)
{
if(pe == null) { Debug.Assert(false); return true; }
if(pe.CustomData.Count > 0) { bCustomData = true; return false; }
return true;
};
gh(m_pwDatabase.RootGroup);
m_pwDatabase.RootGroup.TraverseTree(TraversalMethod.PreOrder, gh, eh);
if(bCustomData) return FileVersion32;
pgDataSource.TraverseTree(TraversalMethod.PreOrder, null, eh); return FileVersion32_3; // KDBX 3.1 is sufficient
} }
private void BinPoolAdd(ProtectedBinaryDictionary dict) private void ComputeKeys(out byte[] pbCipherKey, int cbCipherKey,
out byte[] pbHmacKey64)
{ {
foreach(KeyValuePair<string, ProtectedBinary> kvp in dict) byte[] pbCmp = new byte[32 + 32 + 1];
try
{ {
BinPoolAdd(kvp.Value); Debug.Assert(m_pbMasterSeed != null);
if(m_pbMasterSeed == null)
throw new ArgumentNullException("m_pbMasterSeed");
Debug.Assert(m_pbMasterSeed.Length == 32);
if(m_pbMasterSeed.Length != 32)
throw new FormatException(KLRes.MasterSeedLengthInvalid);
Array.Copy(m_pbMasterSeed, 0, pbCmp, 0, 32);
Debug.Assert(m_pwDatabase != null);
Debug.Assert(m_pwDatabase.MasterKey != null);
ProtectedBinary pbinUser = m_pwDatabase.MasterKey.GenerateKey32(
m_pwDatabase.KdfParameters);
Debug.Assert(pbinUser != null);
if(pbinUser == null)
throw new SecurityException(KLRes.InvalidCompositeKey);
byte[] pUserKey32 = pbinUser.ReadData();
if((pUserKey32 == null) || (pUserKey32.Length != 32))
throw new SecurityException(KLRes.InvalidCompositeKey);
Array.Copy(pUserKey32, 0, pbCmp, 32, 32);
MemUtil.ZeroByteArray(pUserKey32);
pbCipherKey = CryptoUtil.ResizeKey(pbCmp, 0, 64, cbCipherKey);
pbCmp[64] = 1;
#if ModernKeePassLib
var sha256 = HashAlgorithmProvider.OpenAlgorithm(HashAlgorithmNames.Sha256);
var buffer = sha256.HashData(CryptographicBuffer.CreateFromByteArray(pbCmp));
CryptographicBuffer.CopyToByteArray(buffer, out pbHmacKey64);
#else
using(SHA512Managed h = new SHA512Managed())
{
pbHmacKey64 = h.ComputeHash(pbCmp);
}
#endif
}
finally { MemUtil.ZeroByteArray(pbCmp); }
}
private ICipherEngine GetCipher(out int cbEncKey, out int cbEncIV)
{
PwUuid pu = m_pwDatabase.DataCipherUuid;
ICipherEngine iCipher = CipherPool.GlobalPool.GetCipher(pu);
if(iCipher == null) // CryptographicExceptions are translated to "file corrupted"
throw new Exception(KLRes.FileUnknownCipher +
Environment.NewLine + KLRes.FileNewVerOrPlgReq +
Environment.NewLine + "UUID: " + pu.ToHexString() + ".");
ICipherEngine2 iCipher2 = (iCipher as ICipherEngine2);
if(iCipher2 != null)
{
cbEncKey = iCipher2.KeyLength;
if(cbEncKey < 0) throw new InvalidOperationException("EncKey.Length");
cbEncIV = iCipher2.IVLength;
if(cbEncIV < 0) throw new InvalidOperationException("EncIV.Length");
} }
} else
private void BinPoolAdd(ProtectedBinary pb)
{
if(pb == null) { Debug.Assert(false); return; }
if(BinPoolFind(pb) != null) return; // Exists already
m_dictBinPool.Add(m_dictBinPool.Count.ToString(
NumberFormatInfo.InvariantInfo), pb);
}
private string BinPoolFind(ProtectedBinary pb)
{
if(pb == null) { Debug.Assert(false); return null; }
foreach(KeyValuePair<string, ProtectedBinary> kvp in m_dictBinPool)
{ {
if(pb.Equals(kvp.Value)) return kvp.Key; cbEncKey = 32;
cbEncIV = 16;
} }
return null; return iCipher;
} }
private ProtectedBinary BinPoolGet(string strKey) private Stream EncryptStream(Stream s, ICipherEngine iCipher,
byte[] pbKey, int cbIV, bool bEncrypt)
{ {
if(strKey == null) { Debug.Assert(false); return null; } byte[] pbIV = (m_pbEncryptionIV ?? MemUtil.EmptyByteArray);
if(pbIV.Length != cbIV)
{
Debug.Assert(false);
throw new Exception(KLRes.FileCorrupted);
}
ProtectedBinary pb; if(bEncrypt)
if(m_dictBinPool.TryGetValue(strKey, out pb)) return pb; return iCipher.EncryptStream(s, pbKey, pbIV);
return iCipher.DecryptStream(s, pbKey, pbIV);
}
return null; private byte[] ComputeHeaderHmac(byte[] pbHeader, byte[] pbKey)
{
byte[] pbHeaderHmac;
byte[] pbBlockKey = HmacBlockStream.GetHmacKey64(
pbKey, ulong.MaxValue);
#if ModernKeePassLib
var h = MacAlgorithmProvider.OpenAlgorithm(MacAlgorithmNames.HmacSha256).CreateHash(CryptographicBuffer.CreateFromByteArray(pbHeader));
CryptographicBuffer.CopyToByteArray(h.GetValueAndReset(), out pbHeaderHmac);
#else
using (HMACSHA256 h = new HMACSHA256(pbBlockKey))
{
pbHeaderHmac = h.ComputeHash(pbHeader);
}
#endif
MemUtil.ZeroByteArray(pbBlockKey);
return pbHeaderHmac;
}
private void CloseStreams(List<Stream> lStreams)
{
if(lStreams == null) { Debug.Assert(false); return; }
// Typically, closing a stream also closes its base
// stream; however, there may be streams that do not
// do this (e.g. some cipher plugin), thus for safety
// we close all streams manually, from the innermost
// to the outermost
for(int i = lStreams.Count - 1; i >= 0; --i)
{
// Check for duplicates
Debug.Assert((lStreams.IndexOf(lStreams[i]) == i) &&
(lStreams.LastIndexOf(lStreams[i]) == i));
try { lStreams[i].Dispose(); }
catch(Exception) { Debug.Assert(false); }
}
// Do not clear the list
}
private void CleanUpInnerRandomStream()
{
if(m_randomStream != null) m_randomStream.Dispose();
if(m_pbInnerRandomStreamKey != null)
MemUtil.ZeroByteArray(m_pbInnerRandomStreamKey);
} }
private static void SaveBinary(string strName, ProtectedBinary pb, private static void SaveBinary(string strName, ProtectedBinary pb,
@@ -368,22 +513,22 @@ namespace ModernKeePassLib.Serialization
string strPath; string strPath;
int iTry = 1; int iTry = 1;
do do
{ {
strPath = UrlUtil.EnsureTerminatingSeparator(strSaveDir, false); strPath = UrlUtil.EnsureTerminatingSeparator(strSaveDir, false);
string strExt = UrlUtil.GetExtension(strName); string strExt = UrlUtil.GetExtension(strName);
string strDesc = UrlUtil.StripExtension(strName); string strDesc = UrlUtil.StripExtension(strName);
strPath += strDesc; strPath += strDesc;
if (iTry > 1) if(iTry > 1)
strPath += " (" + iTry.ToString(NumberFormatInfo.InvariantInfo) + strPath += " (" + iTry.ToString(NumberFormatInfo.InvariantInfo) +
")"; ")";
if (!string.IsNullOrEmpty(strExt)) strPath += "." + strExt; if(!string.IsNullOrEmpty(strExt)) strPath += "." + strExt;
++iTry; ++iTry;
} }
#if ModernKeePassLib #if ModernKeePassLib
//while(FileSystem.Current.GetFileFromPathAsync(strPath).Result != null); //while(FileSystem.Current.GetFileFromPathAsync(strPath).Result != null);
while (StorageFile.GetFileFromPathAsync(strPath).GetResults() != null); while (StorageFile.GetFileFromPathAsync(strPath).GetResults() != null);

View File

@@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2014 Dominik Reichl <dominik.reichl@t-online.de> Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -45,7 +45,7 @@ namespace ModernKeePassLib.Serialization
(@" (" + m_strFormat + @")") : string.Empty) + "."; (@" (" + m_strFormat + @")") : string.Empty) + ".";
if(m_type == OldFormatType.KeePass1x) if(m_type == OldFormatType.KeePass1x)
str += Environment.NewLine + Environment.NewLine + KLRes.KeePass1xHint; str += Environment.NewLine + KLRes.KeePass1xHint;
return str; return str;
} }

View File

@@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2014 Dominik Reichl <dominik.reichl@t-online.de> Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -331,11 +331,9 @@ namespace ModernKeePassLib.Translation
WriteControlDependentParams(sb, c); WriteControlDependentParams(sb, c);
byte[] pb = StrUtil.Utf8.GetBytes(sb.ToString()); byte[] pb = StrUtil.Utf8.GetBytes(sb.ToString());
byte[] pbSha = CryptoUtil.HashSha256(pb);
SHA256Managed sha256 = new SHA256Managed(); // See also MatchHash
byte[] pbSha = sha256.ComputeHash(pb);
// Also see MatchHash
return "v1:" + Convert.ToBase64String(pbSha, 0, 3, return "v1:" + Convert.ToBase64String(pbSha, 0, 3,
Base64FormattingOptions.None); Base64FormattingOptions.None);
} }

View File

@@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2014 Dominik Reichl <dominik.reichl@t-online.de> Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -19,6 +19,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.Text; using System.Text;
using System.Windows.Forms; using System.Windows.Forms;
using System.Xml.Serialization; using System.Xml.Serialization;

View File

@@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2014 Dominik Reichl <dominik.reichl@t-online.de> Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by

View File

@@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2014 Dominik Reichl <dominik.reichl@t-online.de> Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by

View File

@@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2014 Dominik Reichl <dominik.reichl@t-online.de> Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -92,18 +92,25 @@ namespace ModernKeePassLib.Translation
} }
} }
public static void SaveToFile(KPTranslation kpTrl, string strFileName, public static void Save(KPTranslation kpTrl, string strFileName,
IXmlSerializerEx xs)
{
using(FileStream fs = new FileStream(strFileName, FileMode.Create,
FileAccess.Write, FileShare.None))
{
Save(kpTrl, fs, xs);
}
}
public static void Save(KPTranslation kpTrl, Stream sOut,
IXmlSerializerEx xs) IXmlSerializerEx xs)
{ {
if(xs == null) throw new ArgumentNullException("xs"); if(xs == null) throw new ArgumentNullException("xs");
FileStream fs = new FileStream(strFileName, FileMode.Create,
FileAccess.Write, FileShare.None);
#if !KeePassLibSD #if !KeePassLibSD
GZipStream gz = new GZipStream(fs, CompressionMode.Compress); GZipStream gz = new GZipStream(sOut, CompressionMode.Compress);
#else #else
GZipOutputStream gz = new GZipOutputStream(fs); GZipOutputStream gz = new GZipOutputStream(sOut);
#endif #endif
XmlWriterSettings xws = new XmlWriterSettings(); XmlWriterSettings xws = new XmlWriterSettings();
@@ -118,27 +125,36 @@ namespace ModernKeePassLib.Translation
xw.Close(); xw.Close();
gz.Close(); gz.Close();
fs.Close(); sOut.Close();
} }
public static KPTranslation LoadFromFile(string strFile, public static KPTranslation Load(string strFile, IXmlSerializerEx xs)
IXmlSerializerEx xs) {
KPTranslation kpTrl = null;
using(FileStream fs = new FileStream(strFile, FileMode.Open,
FileAccess.Read, FileShare.Read))
{
kpTrl = Load(fs, xs);
}
return kpTrl;
}
public static KPTranslation Load(Stream s, IXmlSerializerEx xs)
{ {
if(xs == null) throw new ArgumentNullException("xs"); if(xs == null) throw new ArgumentNullException("xs");
FileStream fs = new FileStream(strFile, FileMode.Open,
FileAccess.Read, FileShare.Read);
#if !KeePassLibSD #if !KeePassLibSD
GZipStream gz = new GZipStream(fs, CompressionMode.Decompress); GZipStream gz = new GZipStream(s, CompressionMode.Decompress);
#else #else
GZipInputStream gz = new GZipInputStream(fs); GZipInputStream gz = new GZipInputStream(s);
#endif #endif
KPTranslation kpTrl = (xs.Deserialize(gz) as KPTranslation); KPTranslation kpTrl = (xs.Deserialize(gz) as KPTranslation);
gz.Close(); gz.Close();
fs.Close(); s.Close();
return kpTrl; return kpTrl;
} }
@@ -205,31 +221,76 @@ namespace ModernKeePassLib.Translation
((TrackBar)c).RightToLeftLayout = true; ((TrackBar)c).RightToLeftLayout = true;
else if(c is TreeView) else if(c is TreeView)
((TreeView)c).RightToLeftLayout = true; ((TreeView)c).RightToLeftLayout = true;
else if(c is ToolStrip) // else if(c is ToolStrip)
RtlApplyToToolStripItems(((ToolStrip)c).Items); // RtlApplyToToolStripItems(((ToolStrip)c).Items);
/* else if(c is Button) // Also see Label
{
Button btn = (c as Button);
Image img = btn.Image;
if(img != null)
{
Image imgNew = (Image)img.Clone();
imgNew.RotateFlip(RotateFlipType.RotateNoneFlipX);
btn.Image = imgNew;
}
}
else if(c is Label) // Also see Button
{
Label lbl = (c as Label);
Image img = lbl.Image;
if(img != null)
{
Image imgNew = (Image)img.Clone();
imgNew.RotateFlip(RotateFlipType.RotateNoneFlipX);
lbl.Image = imgNew;
}
} */
if((c is GroupBox) || (c is Panel)) RtlMoveChildControls(c); if(IsRtlMoveChildsRequired(c)) RtlMoveChildControls(c);
} }
} }
internal static bool IsRtlMoveChildsRequired(Control c)
{
if(c == null) { Debug.Assert(false); return false; }
return ((c is GroupBox) || (c is Panel));
}
private static void RtlMoveChildControls(Control cParent) private static void RtlMoveChildControls(Control cParent)
{ {
int nParentWidth = cParent.Size.Width; int nParentWidth = cParent.Size.Width;
foreach(Control c in cParent.Controls) foreach(Control c in cParent.Controls)
{ {
Point ptCur = c.Location; DockStyle ds = c.Dock;
c.Location = new Point(nParentWidth - c.Size.Width - ptCur.X, ptCur.Y); if(ds == DockStyle.Left)
c.Dock = DockStyle.Right;
else if(ds == DockStyle.Right)
c.Dock = DockStyle.Left;
else
{
Point ptCur = c.Location;
c.Location = new Point(nParentWidth - c.Size.Width - ptCur.X, ptCur.Y);
}
} }
} }
/* private static readonly string[] g_vRtlMirrorItemNames = new string[] { };
private static void RtlApplyToToolStripItems(ToolStripItemCollection tsic) private static void RtlApplyToToolStripItems(ToolStripItemCollection tsic)
{ {
foreach(ToolStripItem tsi in tsic) foreach(ToolStripItem tsi in tsic)
{ {
tsi.RightToLeftAutoMirrorImage = true; if(tsi == null) { Debug.Assert(false); continue; }
if(Array.IndexOf<string>(g_vRtlMirrorItemNames, tsi.Name) >= 0)
tsi.RightToLeftAutoMirrorImage = true;
ToolStripDropDownItem tsdd = (tsi as ToolStripDropDownItem);
if(tsdd != null)
RtlApplyToToolStripItems(tsdd.DropDownItems);
} }
} } */
public void ApplyTo(string strTableName, ToolStripItemCollection tsic) public void ApplyTo(string strTableName, ToolStripItemCollection tsic)
{ {

View File

@@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2014 Dominik Reichl <dominik.reichl@t-online.de> Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by

View File

@@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2014 Dominik Reichl <dominik.reichl@t-online.de> Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -19,9 +19,9 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Diagnostics; using System.Diagnostics;
using System.IO;
using System.Text;
#if !KeePassLibSD #if !KeePassLibSD
using System.IO.Compression; using System.IO.Compression;
@@ -38,8 +38,7 @@ namespace ModernKeePassLib.Utility
public static void Open(string strPrefix) public static void Open(string strPrefix)
{ {
return; // Logging is not enabled in normal builds of KeePass! // Logging is not enabled in normal builds of KeePass!
/* /*
AppLogEx.Close(); AppLogEx.Close();
@@ -58,7 +57,7 @@ namespace ModernKeePassLib.Utility
string strPath = strTemp + strPrefix + "-"; string strPath = strTemp + strPrefix + "-";
Debug.Assert(strPath.IndexOf('/') < 0); Debug.Assert(strPath.IndexOf('/') < 0);
DateTime dtNow = DateTime.Now; DateTime dtNow = DateTime.UtcNow;
string strTime = dtNow.ToString("s"); string strTime = dtNow.ToString("s");
strTime = strTime.Replace('T', '-'); strTime = strTime.Replace('T', '-');
strTime = strTime.Replace(':', '-'); strTime = strTime.Replace(':', '-');

View File

@@ -28,38 +28,79 @@ using System.Drawing;
using System.Drawing.Imaging; using System.Drawing.Imaging;
#endif #endif
using System.Diagnostics; using System.Diagnostics;
using System.Threading.Tasks;
namespace ModernKeePassLib.Utility namespace ModernKeePassLib.Utility
{ {
public static class GfxUtil public static class GfxUtil
{ {
#if KeePassRT #if (!KeePassLibSD && !KeePassUAP)
public static Image LoadImage(byte[] pb) private sealed class GfxImage
{ {
MemoryStream ms = new MemoryStream(pb, false); public byte[] Data;
try { return Image.FromStream(ms); }
finally { ms.Close(); } public int Width;
} public int Height;
#elif ModernKeePassLib
public static IBitmap LoadImage(byte[] pb) public GfxImage(byte[] pbData, int w, int h)
{ {
using (var ms = new MemoryStream(pb, false)) { this.Data = pbData;
return BitmapLoader.Current.Load(ms, null, null).Result; this.Width = w;
this.Height = h;
} }
#if DEBUG
// For debugger display
public override string ToString()
{
return (this.Width.ToString() + "x" + this.Height.ToString());
}
#endif
} }
#else #endif
#if KeePassUAP
public static Image LoadImage(byte[] pb) public static Image LoadImage(byte[] pb)
{ {
if(pb == null) throw new ArgumentNullException("pb"); if(pb == null) throw new ArgumentNullException("pb");
MemoryStream ms = new MemoryStream(pb, false); MemoryStream ms = new MemoryStream(pb, false);
try { return LoadImagePriv(ms); } try { return Image.FromStream(ms); }
catch(Exception) finally { ms.Close(); }
}
#elif ModernKeePassLib
public static async Task<IBitmap> LoadImage(byte[] pb)
{
return await ScaleImage(pb, null, null);
}
public static async Task<IBitmap> ScaleImage(byte[] pb, int? w, int? h)
{
using (var ms = new MemoryStream(pb, false))
{
return await BitmapLoader.Current.Load(ms, w, h);
}
}
#else
public static Image LoadImage(byte[] pb)
{
if(pb == null) throw new ArgumentNullException("pb");
#if !KeePassLibSD
// First try to load the data as ICO and afterwards as
// normal image, because trying to load an ICO using
// the normal image loading methods can result in a
// low resolution image
try
{ {
Image imgIco = TryLoadIco(pb); Image imgIco = ExtractBestImageFromIco(pb);
if(imgIco != null) return imgIco; if(imgIco != null) return imgIco;
throw;
} }
catch(Exception) { Debug.Assert(false); }
#endif
MemoryStream ms = new MemoryStream(pb, false);
try { return LoadImagePriv(ms); }
finally { ms.Close(); } finally { ms.Close(); }
} }
@@ -91,7 +132,12 @@ namespace ModernKeePassLib.Utility
using(Graphics g = Graphics.FromImage(bmp)) using(Graphics g = Graphics.FromImage(bmp))
{ {
g.Clear(Color.Transparent); g.Clear(Color.Transparent);
#if !KeePassLibSD
g.DrawImageUnscaled(imgSrc, 0, 0);
#else
g.DrawImage(imgSrc, 0, 0); g.DrawImage(imgSrc, 0, 0);
#endif
} }
return bmp; return bmp;
@@ -99,17 +145,313 @@ namespace ModernKeePassLib.Utility
finally { if(imgSrc != null) imgSrc.Dispose(); } finally { if(imgSrc != null) imgSrc.Dispose(); }
} }
private static Image TryLoadIco(byte[] pb)
{
#if !KeePassLibSD #if !KeePassLibSD
MemoryStream ms = new MemoryStream(pb, false); private static Image ExtractBestImageFromIco(byte[] pb)
try { return (new Icon(ms)).ToBitmap(); } {
catch(Exception) { } List<GfxImage> l = UnpackIco(pb);
finally { ms.Close(); } if((l == null) || (l.Count == 0)) return null;
#endif
return null; long qMax = 0;
foreach(GfxImage gi in l)
{
if(gi.Width == 0) gi.Width = 256;
if(gi.Height == 0) gi.Height = 256;
qMax = Math.Max(qMax, (long)gi.Width * (long)gi.Height);
}
byte[] pbHdrPng = new byte[] {
0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A
};
byte[] pbHdrJpeg = new byte[] { 0xFF, 0xD8, 0xFF };
Image imgBest = null;
int bppBest = -1;
foreach(GfxImage gi in l)
{
if(((long)gi.Width * (long)gi.Height) < qMax) continue;
byte[] pbImg = gi.Data;
Image img = null;
try
{
if((pbImg.Length > pbHdrPng.Length) &&
MemUtil.ArraysEqual(pbHdrPng,
MemUtil.Mid<byte>(pbImg, 0, pbHdrPng.Length)))
img = GfxUtil.LoadImage(pbImg);
else if((pbImg.Length > pbHdrJpeg.Length) &&
MemUtil.ArraysEqual(pbHdrJpeg,
MemUtil.Mid<byte>(pbImg, 0, pbHdrJpeg.Length)))
img = GfxUtil.LoadImage(pbImg);
else
{
using(MemoryStream ms = new MemoryStream(pb, false))
{
using(Icon ico = new Icon(ms, gi.Width, gi.Height))
{
img = ico.ToBitmap();
}
}
}
}
catch(Exception) { Debug.Assert(false); }
if(img == null) continue;
if((img.Width < gi.Width) || (img.Height < gi.Height))
{
Debug.Assert(false);
img.Dispose();
continue;
}
int bpp = GetBitsPerPixel(img.PixelFormat);
if(bpp > bppBest)
{
if(imgBest != null) imgBest.Dispose();
imgBest = img;
bppBest = bpp;
}
else img.Dispose();
}
return imgBest;
}
private static List<GfxImage> UnpackIco(byte[] pb)
{
if(pb == null) { Debug.Assert(false); return null; }
const int SizeICONDIR = 6;
const int SizeICONDIRENTRY = 16;
if(pb.Length < SizeICONDIR) return null;
if(MemUtil.BytesToUInt16(pb, 0) != 0) return null; // Reserved, 0
if(MemUtil.BytesToUInt16(pb, 2) != 1) return null; // ICO type, 1
int n = MemUtil.BytesToUInt16(pb, 4);
if(n < 0) { Debug.Assert(false); return null; }
int cbDir = SizeICONDIR + (n * SizeICONDIRENTRY);
if(pb.Length < cbDir) return null;
List<GfxImage> l = new List<GfxImage>();
int iOffset = SizeICONDIR;
for(int i = 0; i < n; ++i)
{
int w = pb[iOffset];
int h = pb[iOffset + 1];
if((w < 0) || (h < 0)) { Debug.Assert(false); return null; }
int cb = MemUtil.BytesToInt32(pb, iOffset + 8);
if(cb <= 0) return null; // Data must have header (even BMP)
int p = MemUtil.BytesToInt32(pb, iOffset + 12);
if(p < cbDir) return null;
if((p + cb) > pb.Length) return null;
try
{
byte[] pbImage = MemUtil.Mid<byte>(pb, p, cb);
GfxImage img = new GfxImage(pbImage, w, h);
l.Add(img);
}
catch(Exception) { Debug.Assert(false); return null; }
iOffset += SizeICONDIRENTRY;
}
return l;
}
private static int GetBitsPerPixel(PixelFormat f)
{
int bpp = 0;
switch(f)
{
case PixelFormat.Format1bppIndexed:
bpp = 1;
break;
case PixelFormat.Format4bppIndexed:
bpp = 4;
break;
case PixelFormat.Format8bppIndexed:
bpp = 8;
break;
case PixelFormat.Format16bppArgb1555:
case PixelFormat.Format16bppGrayScale:
case PixelFormat.Format16bppRgb555:
case PixelFormat.Format16bppRgb565:
bpp = 16;
break;
case PixelFormat.Format24bppRgb:
bpp = 24;
break;
case PixelFormat.Format32bppArgb:
case PixelFormat.Format32bppPArgb:
case PixelFormat.Format32bppRgb:
bpp = 32;
break;
case PixelFormat.Format48bppRgb:
bpp = 48;
break;
case PixelFormat.Format64bppArgb:
case PixelFormat.Format64bppPArgb:
bpp = 64;
break;
default:
Debug.Assert(false);
break;
}
return bpp;
}
public static Image ScaleImage(Image img, int w, int h)
{
return ScaleImage(img, w, h, ScaleTransformFlags.None);
}
/// <summary>
/// Resize an image.
/// </summary>
/// <param name="img">Image to resize.</param>
/// <param name="w">Width of the returned image.</param>
/// <param name="h">Height of the returned image.</param>
/// <param name="f">Flags to customize scaling behavior.</param>
/// <returns>Resized image. This object is always different
/// from <paramref name="img" /> (i.e. they can be
/// disposed separately).</returns>
public static Image ScaleImage(Image img, int w, int h,
ScaleTransformFlags f)
{
if(img == null) throw new ArgumentNullException("img");
if(w < 0) throw new ArgumentOutOfRangeException("w");
if(h < 0) throw new ArgumentOutOfRangeException("h");
bool bUIIcon = ((f & ScaleTransformFlags.UIIcon) !=
ScaleTransformFlags.None);
// We must return a Bitmap object for UIUtil.CreateScaledImage
Bitmap bmp = new Bitmap(w, h, PixelFormat.Format32bppArgb);
using(Graphics g = Graphics.FromImage(bmp))
{
g.Clear(Color.Transparent);
g.SmoothingMode = SmoothingMode.HighQuality;
g.CompositingQuality = CompositingQuality.HighQuality;
int wSrc = img.Width;
int hSrc = img.Height;
InterpolationMode im = InterpolationMode.HighQualityBicubic;
if((wSrc > 0) && (hSrc > 0))
{
if(bUIIcon && ((w % wSrc) == 0) && ((h % hSrc) == 0))
im = InterpolationMode.NearestNeighbor;
// else if((w < wSrc) && (h < hSrc))
// im = InterpolationMode.HighQualityBilinear;
}
else { Debug.Assert(false); }
g.InterpolationMode = im;
RectangleF rSource = new RectangleF(0.0f, 0.0f, wSrc, hSrc);
RectangleF rDest = new RectangleF(0.0f, 0.0f, w, h);
AdjustScaleRects(ref rSource, ref rDest);
g.DrawImage(img, rDest, rSource, GraphicsUnit.Pixel);
}
return bmp;
}
internal static void AdjustScaleRects(ref RectangleF rSource,
ref RectangleF rDest)
{
// When enlarging images, apply a -0.5 offset to avoid
// the scaled image being cropped on the top/left side;
// when shrinking images, do not apply a -0.5 offset,
// otherwise the image is cropped on the bottom/right
// side; this applies to all interpolation modes
if(rDest.Width > rSource.Width)
rSource.X = rSource.X - 0.5f;
if(rDest.Height > rSource.Height)
rSource.Y = rSource.Y - 0.5f;
// When shrinking, apply a +0.5 offset, such that the
// scaled image is less cropped on the bottom/right side
if(rDest.Width < rSource.Width)
rSource.X = rSource.X + 0.5f;
if(rDest.Height < rSource.Height)
rSource.Y = rSource.Y + 0.5f;
}
#if DEBUG
public static Image ScaleTest(Image[] vIcons)
{
Bitmap bmp = new Bitmap(1024, vIcons.Length * (256 + 12),
PixelFormat.Format32bppArgb);
using(Graphics g = Graphics.FromImage(bmp))
{
g.Clear(Color.White);
int[] v = new int[] { 16, 24, 32, 48, 64, 128, 256 };
int x;
int y = 8;
foreach(Image imgIcon in vIcons)
{
if(imgIcon == null) { Debug.Assert(false); continue; }
x = 128;
foreach(int q in v)
{
using(Image img = ScaleImage(imgIcon, q, q,
ScaleTransformFlags.UIIcon))
{
g.DrawImageUnscaled(img, x, y);
}
x += q + 8;
}
y += v[v.Length - 1] + 8;
}
}
return bmp;
}
#endif // DEBUG
#endif // !KeePassLibSD
#endif // KeePassUAP
#if ModernKeePassLib
#else
internal static string ImageToDataUri(Image img)
{
if(img == null) { Debug.Assert(false); return string.Empty; }
byte[] pb = null;
using(MemoryStream ms = new MemoryStream())
{
img.Save(ms, ImageFormat.Png);
pb = ms.ToArray();
}
return StrUtil.DataToDataUri(pb, "image/png");
} }
#endif #endif
} }
} }

View File

@@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2014 Dominik Reichl <dominik.reichl@t-online.de> Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -19,14 +19,15 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Runtime.CompilerServices;
using System.Text;
#if !KeePassLibSD #if KeePassLibSD
using System.IO.Compression;
#else
using KeePassLibSD; using KeePassLibSD;
#else
using System.IO.Compression;
#endif #endif
namespace ModernKeePassLib.Utility namespace ModernKeePassLib.Utility
@@ -36,6 +37,8 @@ namespace ModernKeePassLib.Utility
/// </summary> /// </summary>
public static class MemUtil public static class MemUtil
{ {
internal static readonly byte[] EmptyByteArray = new byte[0];
private static readonly uint[] m_vSBox = new uint[256] { private static readonly uint[] m_vSBox = new uint[256] {
0xCD2FACB3, 0xE78A7F5C, 0x6F0803FC, 0xBCF6E230, 0xCD2FACB3, 0xE78A7F5C, 0x6F0803FC, 0xBCF6E230,
0x3A321712, 0x06403DB1, 0xD2F84B95, 0xDF22A6E4, 0x3A321712, 0x06403DB1, 0xD2F84B95, 0xDF22A6E4,
@@ -253,66 +256,139 @@ namespace ModernKeePassLib.Utility
Debug.Assert(pbArray != null); Debug.Assert(pbArray != null);
if(pbArray == null) throw new ArgumentNullException("pbArray"); if(pbArray == null) throw new ArgumentNullException("pbArray");
// for(int i = 0; i < pbArray.Length; ++i)
// pbArray[i] = 0;
Array.Clear(pbArray, 0, pbArray.Length); Array.Clear(pbArray, 0, pbArray.Length);
} }
/// <summary> /// <summary>
/// Convert 2 bytes to a 16-bit unsigned integer using Little-Endian /// Set all elements of an array to the default value.
/// encoding. /// </summary>
/// <param name="v">Input array.</param>
#if KeePassLibSD
[MethodImpl(MethodImplOptions.NoInlining)]
#else
[MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
#endif
public static void ZeroArray<T>(T[] v)
{
if(v == null) { Debug.Assert(false); throw new ArgumentNullException("v"); }
Array.Clear(v, 0, v.Length);
}
/// <summary>
/// Convert 2 bytes to a 16-bit unsigned integer (little-endian).
/// </summary> /// </summary>
/// <param name="pb">Input bytes. Array must contain at least 2 bytes.</param>
/// <returns>16-bit unsigned integer.</returns>
public static ushort BytesToUInt16(byte[] pb) public static ushort BytesToUInt16(byte[] pb)
{ {
Debug.Assert((pb != null) && (pb.Length == 2)); Debug.Assert((pb != null) && (pb.Length == 2));
if(pb == null) throw new ArgumentNullException("pb"); if(pb == null) throw new ArgumentNullException("pb");
if(pb.Length != 2) throw new ArgumentException(); if(pb.Length != 2) throw new ArgumentOutOfRangeException("pb");
return (ushort)((ushort)pb[0] | ((ushort)pb[1] << 8)); return (ushort)((ushort)pb[0] | ((ushort)pb[1] << 8));
} }
/// <summary> /// <summary>
/// Convert 4 bytes to a 32-bit unsigned integer using Little-Endian /// Convert 2 bytes to a 16-bit unsigned integer (little-endian).
/// encoding. /// </summary>
public static ushort BytesToUInt16(byte[] pb, int iOffset)
{
if(pb == null) { Debug.Assert(false); throw new ArgumentNullException("pb"); }
if((iOffset < 0) || ((iOffset + 1) >= pb.Length))
{
Debug.Assert(false);
throw new ArgumentOutOfRangeException("iOffset");
}
return (ushort)((ushort)pb[iOffset] | ((ushort)pb[iOffset + 1] << 8));
}
/// <summary>
/// Convert 4 bytes to a 32-bit unsigned integer (little-endian).
/// </summary> /// </summary>
/// <param name="pb">Input bytes.</param>
/// <returns>32-bit unsigned integer.</returns>
public static uint BytesToUInt32(byte[] pb) public static uint BytesToUInt32(byte[] pb)
{ {
Debug.Assert((pb != null) && (pb.Length == 4)); Debug.Assert((pb != null) && (pb.Length == 4));
if(pb == null) throw new ArgumentNullException("pb"); if(pb == null) throw new ArgumentNullException("pb");
if(pb.Length != 4) throw new ArgumentException("Input array must contain 4 bytes!"); if(pb.Length != 4) throw new ArgumentOutOfRangeException("pb");
return (uint)pb[0] | ((uint)pb[1] << 8) | ((uint)pb[2] << 16) | return ((uint)pb[0] | ((uint)pb[1] << 8) | ((uint)pb[2] << 16) |
((uint)pb[3] << 24); ((uint)pb[3] << 24));
} }
/// <summary> /// <summary>
/// Convert 8 bytes to a 64-bit unsigned integer using Little-Endian /// Convert 4 bytes to a 32-bit unsigned integer (little-endian).
/// encoding. /// </summary>
public static uint BytesToUInt32(byte[] pb, int iOffset)
{
if(pb == null) { Debug.Assert(false); throw new ArgumentNullException("pb"); }
if((iOffset < 0) || ((iOffset + 3) >= pb.Length))
{
Debug.Assert(false);
throw new ArgumentOutOfRangeException("iOffset");
}
return ((uint)pb[iOffset] | ((uint)pb[iOffset + 1] << 8) |
((uint)pb[iOffset + 2] << 16) | ((uint)pb[iOffset + 3] << 24));
}
/// <summary>
/// Convert 8 bytes to a 64-bit unsigned integer (little-endian).
/// </summary> /// </summary>
/// <param name="pb">Input bytes.</param>
/// <returns>64-bit unsigned integer.</returns>
public static ulong BytesToUInt64(byte[] pb) public static ulong BytesToUInt64(byte[] pb)
{ {
Debug.Assert((pb != null) && (pb.Length == 8)); Debug.Assert((pb != null) && (pb.Length == 8));
if(pb == null) throw new ArgumentNullException("pb"); if(pb == null) throw new ArgumentNullException("pb");
if(pb.Length != 8) throw new ArgumentException(); if(pb.Length != 8) throw new ArgumentOutOfRangeException("pb");
return (ulong)pb[0] | ((ulong)pb[1] << 8) | ((ulong)pb[2] << 16) | return ((ulong)pb[0] | ((ulong)pb[1] << 8) | ((ulong)pb[2] << 16) |
((ulong)pb[3] << 24) | ((ulong)pb[4] << 32) | ((ulong)pb[5] << 40) | ((ulong)pb[3] << 24) | ((ulong)pb[4] << 32) | ((ulong)pb[5] << 40) |
((ulong)pb[6] << 48) | ((ulong)pb[7] << 56); ((ulong)pb[6] << 48) | ((ulong)pb[7] << 56));
} }
/// <summary> /// <summary>
/// Convert a 16-bit unsigned integer to 2 bytes using Little-Endian /// Convert 8 bytes to a 64-bit unsigned integer (little-endian).
/// encoding. /// </summary>
public static ulong BytesToUInt64(byte[] pb, int iOffset)
{
if(pb == null) { Debug.Assert(false); throw new ArgumentNullException("pb"); }
if((iOffset < 0) || ((iOffset + 7) >= pb.Length))
{
Debug.Assert(false);
throw new ArgumentOutOfRangeException("iOffset");
}
// if(BitConverter.IsLittleEndian)
// return BitConverter.ToUInt64(pb, iOffset);
return ((ulong)pb[iOffset] | ((ulong)pb[iOffset + 1] << 8) |
((ulong)pb[iOffset + 2] << 16) | ((ulong)pb[iOffset + 3] << 24) |
((ulong)pb[iOffset + 4] << 32) | ((ulong)pb[iOffset + 5] << 40) |
((ulong)pb[iOffset + 6] << 48) | ((ulong)pb[iOffset + 7] << 56));
}
public static int BytesToInt32(byte[] pb)
{
return (int)BytesToUInt32(pb);
}
public static int BytesToInt32(byte[] pb, int iOffset)
{
return (int)BytesToUInt32(pb, iOffset);
}
public static long BytesToInt64(byte[] pb)
{
return (long)BytesToUInt64(pb);
}
public static long BytesToInt64(byte[] pb, int iOffset)
{
return (long)BytesToUInt64(pb, iOffset);
}
/// <summary>
/// Convert a 16-bit unsigned integer to 2 bytes (little-endian).
/// </summary> /// </summary>
/// <param name="uValue">16-bit input word.</param>
/// <returns>Two bytes representing the 16-bit value.</returns>
public static byte[] UInt16ToBytes(ushort uValue) public static byte[] UInt16ToBytes(ushort uValue)
{ {
byte[] pb = new byte[2]; byte[] pb = new byte[2];
@@ -327,11 +403,8 @@ namespace ModernKeePassLib.Utility
} }
/// <summary> /// <summary>
/// Convert a 32-bit unsigned integer to 4 bytes using Little-Endian /// Convert a 32-bit unsigned integer to 4 bytes (little-endian).
/// encoding.
/// </summary> /// </summary>
/// <param name="uValue">32-bit input word.</param>
/// <returns>Four bytes representing the 32-bit value.</returns>
public static byte[] UInt32ToBytes(uint uValue) public static byte[] UInt32ToBytes(uint uValue)
{ {
byte[] pb = new byte[4]; byte[] pb = new byte[4];
@@ -348,11 +421,29 @@ namespace ModernKeePassLib.Utility
} }
/// <summary> /// <summary>
/// Convert a 64-bit unsigned integer to 8 bytes using Little-Endian /// Convert a 32-bit unsigned integer to 4 bytes (little-endian).
/// encoding. /// </summary>
public static void UInt32ToBytesEx(uint uValue, byte[] pb, int iOffset)
{
if(pb == null) { Debug.Assert(false); throw new ArgumentNullException("pb"); }
if((iOffset < 0) || ((iOffset + 3) >= pb.Length))
{
Debug.Assert(false);
throw new ArgumentOutOfRangeException("iOffset");
}
unchecked
{
pb[iOffset] = (byte)uValue;
pb[iOffset + 1] = (byte)(uValue >> 8);
pb[iOffset + 2] = (byte)(uValue >> 16);
pb[iOffset + 3] = (byte)(uValue >> 24);
}
}
/// <summary>
/// Convert a 64-bit unsigned integer to 8 bytes (little-endian).
/// </summary> /// </summary>
/// <param name="uValue">64-bit input word.</param>
/// <returns>Eight bytes representing the 64-bit value.</returns>
public static byte[] UInt64ToBytes(ulong uValue) public static byte[] UInt64ToBytes(ulong uValue)
{ {
byte[] pb = new byte[8]; byte[] pb = new byte[8];
@@ -372,6 +463,61 @@ namespace ModernKeePassLib.Utility
return pb; return pb;
} }
/// <summary>
/// Convert a 64-bit unsigned integer to 8 bytes (little-endian).
/// </summary>
public static void UInt64ToBytesEx(ulong uValue, byte[] pb, int iOffset)
{
if(pb == null) { Debug.Assert(false); throw new ArgumentNullException("pb"); }
if((iOffset < 0) || ((iOffset + 7) >= pb.Length))
{
Debug.Assert(false);
throw new ArgumentOutOfRangeException("iOffset");
}
unchecked
{
pb[iOffset] = (byte)uValue;
pb[iOffset + 1] = (byte)(uValue >> 8);
pb[iOffset + 2] = (byte)(uValue >> 16);
pb[iOffset + 3] = (byte)(uValue >> 24);
pb[iOffset + 4] = (byte)(uValue >> 32);
pb[iOffset + 5] = (byte)(uValue >> 40);
pb[iOffset + 6] = (byte)(uValue >> 48);
pb[iOffset + 7] = (byte)(uValue >> 56);
}
}
public static byte[] Int32ToBytes(int iValue)
{
return UInt32ToBytes((uint)iValue);
}
public static byte[] Int64ToBytes(long lValue)
{
return UInt64ToBytes((ulong)lValue);
}
public static uint RotateLeft32(uint u, int nBits)
{
return ((u << nBits) | (u >> (32 - nBits)));
}
public static uint RotateRight32(uint u, int nBits)
{
return ((u >> nBits) | (u << (32 - nBits)));
}
public static ulong RotateLeft64(ulong u, int nBits)
{
return ((u << nBits) | (u >> (64 - nBits)));
}
public static ulong RotateRight64(ulong u, int nBits)
{
return ((u >> nBits) | (u << (64 - nBits)));
}
public static bool ArraysEqual(byte[] x, byte[] y) public static bool ArraysEqual(byte[] x, byte[] y)
{ {
// Return false if one of them is null (not comparable)! // Return false if one of them is null (not comparable)!
@@ -387,19 +533,21 @@ namespace ModernKeePassLib.Utility
return true; return true;
} }
public static void XorArray(byte[] pbSource, int nSourceOffset, public static void XorArray(byte[] pbSource, int iSourceOffset,
byte[] pbBuffer, int nBufferOffset, int nLength) byte[] pbBuffer, int iBufferOffset, int cb)
{ {
if(pbSource == null) throw new ArgumentNullException("pbSource"); if(pbSource == null) throw new ArgumentNullException("pbSource");
if(nSourceOffset < 0) throw new ArgumentException(); if(iSourceOffset < 0) throw new ArgumentOutOfRangeException("iSourceOffset");
if(pbBuffer == null) throw new ArgumentNullException("pbBuffer"); if(pbBuffer == null) throw new ArgumentNullException("pbBuffer");
if(nBufferOffset < 0) throw new ArgumentException(); if(iBufferOffset < 0) throw new ArgumentOutOfRangeException("iBufferOffset");
if(nLength < 0) throw new ArgumentException(); if(cb < 0) throw new ArgumentOutOfRangeException("cb");
if((nSourceOffset + nLength) > pbSource.Length) throw new ArgumentException(); if(iSourceOffset > (pbSource.Length - cb))
if((nBufferOffset + nLength) > pbBuffer.Length) throw new ArgumentException(); throw new ArgumentOutOfRangeException("cb");
if(iBufferOffset > (pbBuffer.Length - cb))
throw new ArgumentOutOfRangeException("cb");
for(int i = 0; i < nLength; ++i) for(int i = 0; i < cb; ++i)
pbBuffer[nBufferOffset + i] ^= pbSource[nSourceOffset + i]; pbBuffer[iBufferOffset + i] ^= pbSource[iSourceOffset + i];
} }
/// <summary> /// <summary>
@@ -478,7 +626,8 @@ namespace ModernKeePassLib.Utility
if(s == null) { Debug.Assert(false); return; } if(s == null) { Debug.Assert(false); return; }
if(pbData == null) { Debug.Assert(false); return; } if(pbData == null) { Debug.Assert(false); return; }
s.Write(pbData, 0, pbData.Length); Debug.Assert(pbData.Length >= 0);
if(pbData.Length > 0) s.Write(pbData, 0, pbData.Length);
} }
public static byte[] Compress(byte[] pbData) public static byte[] Compress(byte[] pbData)
@@ -486,15 +635,21 @@ namespace ModernKeePassLib.Utility
if(pbData == null) throw new ArgumentNullException("pbData"); if(pbData == null) throw new ArgumentNullException("pbData");
if(pbData.Length == 0) return pbData; if(pbData.Length == 0) return pbData;
MemoryStream msCompressed = new MemoryStream(); byte[] pbCompressed;
GZipStream gz = new GZipStream(msCompressed, CompressionMode.Compress); using(MemoryStream msSource = new MemoryStream(pbData, false))
MemoryStream msSource = new MemoryStream(pbData, false); {
MemUtil.CopyStream(msSource, gz); using(MemoryStream msCompressed = new MemoryStream())
gz.Dispose(); {
msSource.Dispose(); using(GZipStream gz = new GZipStream(msCompressed,
CompressionMode.Compress))
{
MemUtil.CopyStream(msSource, gz);
}
pbCompressed = msCompressed.ToArray();
}
}
byte[] pbCompressed = msCompressed.ToArray();
msCompressed.Dispose();
return pbCompressed; return pbCompressed;
} }
@@ -503,15 +658,21 @@ namespace ModernKeePassLib.Utility
if(pbCompressed == null) throw new ArgumentNullException("pbCompressed"); if(pbCompressed == null) throw new ArgumentNullException("pbCompressed");
if(pbCompressed.Length == 0) return pbCompressed; if(pbCompressed.Length == 0) return pbCompressed;
MemoryStream msCompressed = new MemoryStream(pbCompressed, false); byte[] pbData;
GZipStream gz = new GZipStream(msCompressed, CompressionMode.Decompress); using(MemoryStream msData = new MemoryStream())
MemoryStream msData = new MemoryStream(); {
MemUtil.CopyStream(gz, msData); using(MemoryStream msCompressed = new MemoryStream(pbCompressed, false))
gz.Dispose(); {
msCompressed.Dispose(); using(GZipStream gz = new GZipStream(msCompressed,
CompressionMode.Decompress))
{
MemUtil.CopyStream(gz, msData);
}
}
pbData = msData.ToArray();
}
byte[] pbData = msData.ToArray();
msData.Dispose();
return pbData; return pbData;
} }

Some files were not shown because too many files have changed in this diff Show More