mirror of
https://github.com/wismna/ModernKeePass.git
synced 2025-12-06 00:02:43 -05:00
WIP Update lib to 2.37
This commit is contained in:
@@ -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();
|
||||||
|
|||||||
@@ -53,8 +53,10 @@ 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)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
174
ModernKeePassLib/Collections/ProtectedBinarySet.cs
Normal file
174
ModernKeePassLib/Collections/ProtectedBinarySet.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
415
ModernKeePassLib/Collections/VariantDictionary.cs
Normal file
415
ModernKeePassLib/Collections/VariantDictionary.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
254
ModernKeePassLib/Cryptography/Cipher/ChaCha20Cipher.cs
Normal file
254
ModernKeePassLib/Cryptography/Cipher/ChaCha20Cipher.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
177
ModernKeePassLib/Cryptography/Cipher/ChaCha20Engine.cs
Normal file
177
ModernKeePassLib/Cryptography/Cipher/ChaCha20Engine.cs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
109
ModernKeePassLib/Cryptography/Cipher/CtrBlockCipher.cs
Normal file
109
ModernKeePassLib/Cryptography/Cipher/CtrBlockCipher.cs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Dispose(bool bDisposing)
|
base.Dispose(bDisposing);
|
||||||
{
|
|
||||||
// Clear sensitive data
|
|
||||||
Array.Clear(m_state, 0, m_state.Length);
|
|
||||||
Array.Clear(m_x, 0, m_x.Length);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void NextOutput()
|
protected override void NextBlock(byte[] pBlock)
|
||||||
{
|
{
|
||||||
uint[] x = m_x; // Local alias for working buffer
|
if(pBlock == null) throw new ArgumentNullException("pBlock");
|
||||||
|
if(pBlock.Length != 64) throw new ArgumentOutOfRangeException("pBlock");
|
||||||
|
|
||||||
// Compiler/runtime might remove array bound checks after this
|
// 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();
|
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
|
||||||
m_pbEntropyPool = shaPool.ComputeHash(pbFinal);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
{
|
||||||
|
m_pbEntropyPool = shaPool.ComputeHash(pbCmp);
|
||||||
|
}
|
||||||
|
#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,17 +178,13 @@ 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);
|
||||||
@@ -187,71 +193,118 @@ namespace ModernKeePassLib.Cryptography
|
|||||||
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];
|
||||||
@@ -265,38 +318,31 @@ 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>
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,9 +256,11 @@ 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;
|
||||||
|
|
||||||
return (nEnd - nStart);
|
return (nEnd - nStart);
|
||||||
|
|||||||
191
ModernKeePassLib/Cryptography/CryptoUtil.cs
Normal file
191
ModernKeePassLib/Cryptography/CryptoUtil.cs
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
280
ModernKeePassLib/Cryptography/Hash/Blake2b.cs
Normal file
280
ModernKeePassLib/Cryptography/Hash/Blake2b.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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,8 +37,8 @@ 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;
|
||||||
@@ -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;
|
||||||
@@ -110,21 +108,13 @@ namespace ModernKeePassLib.Cryptography
|
|||||||
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)
|
||||||
{
|
{
|
||||||
@@ -148,12 +138,18 @@ namespace ModernKeePassLib.Cryptography
|
|||||||
m_pbFinalHash = m_hash.Hash;
|
m_pbFinalHash = m_hash.Hash;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
catch(Exception) { Debug.Assert(false); }
|
catch (Exception)
|
||||||
|
{
|
||||||
m_hash = null;
|
Debug.Assert(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_sBaseStream.Dispose();
|
base.Dispose(disposing);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Flush()
|
||||||
|
{
|
||||||
|
m_sBaseStream.Flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override long Seek(long lOffset, SeekOrigin soOrigin)
|
public override long Seek(long lOffset, SeekOrigin soOrigin)
|
||||||
@@ -186,7 +182,6 @@ namespace ModernKeePassLib.Cryptography
|
|||||||
|
|
||||||
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
|
||||||
@@ -207,8 +202,8 @@ namespace ModernKeePassLib.Cryptography
|
|||||||
|
|
||||||
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
|
||||||
|
|||||||
@@ -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,20 +45,12 @@ 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)))
|
||||||
|
|||||||
402
ModernKeePassLib/Cryptography/KeyDerivation/AesKdf.GCrypt.cs
Normal file
402
ModernKeePassLib/Cryptography/KeyDerivation/AesKdf.GCrypt.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
206
ModernKeePassLib/Cryptography/KeyDerivation/AesKdf.cs
Normal file
206
ModernKeePassLib/Cryptography/KeyDerivation/AesKdf.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
636
ModernKeePassLib/Cryptography/KeyDerivation/Argon2Kdf.Core.cs
Normal file
636
ModernKeePassLib/Cryptography/KeyDerivation/Argon2Kdf.Core.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
144
ModernKeePassLib/Cryptography/KeyDerivation/Argon2Kdf.cs
Normal file
144
ModernKeePassLib/Cryptography/KeyDerivation/Argon2Kdf.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
142
ModernKeePassLib/Cryptography/KeyDerivation/KdfEngine.cs
Normal file
142
ModernKeePassLib/Cryptography/KeyDerivation/KdfEngine.cs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
80
ModernKeePassLib/Cryptography/KeyDerivation/KdfParameters.cs
Normal file
80
ModernKeePassLib/Cryptography/KeyDerivation/KdfParameters.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
96
ModernKeePassLib/Cryptography/KeyDerivation/KdfPool.cs
Normal file
96
ModernKeePassLib/Cryptography/KeyDerivation/KdfPool.cs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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,8 +49,12 @@ 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);
|
||||||
@@ -56,22 +63,42 @@ namespace ModernKeePassLib.Cryptography.PasswordGenerator
|
|||||||
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,
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,29 +129,36 @@ 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,
|
||||||
@@ -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");
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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,17 +61,7 @@ namespace ModernKeePassLib.Keys
|
|||||||
|
|
||||||
if(bPerformHash)
|
if(bPerformHash)
|
||||||
{
|
{
|
||||||
#if ModernKeePassLib
|
byte[] pbRaw = CryptoUtil.HashSha256(pbKeyData);
|
||||||
/*var sha256 = WinRTCrypto.HashAlgorithmProvider.OpenAlgorithm(HashAlgorithm.Sha256);
|
|
||||||
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);
|
m_pbKey = new ProtectedBinary(true, pbRaw);
|
||||||
}
|
}
|
||||||
else m_pbKey = new ProtectedBinary(true, pbKeyData);
|
else m_pbKey = new ProtectedBinary(true, pbKeyData);
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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,18 +74,12 @@ 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
|
||||||
|
|
||||||
|
byte[] pbRaw = CryptoUtil.HashSha256(pbPasswordUtf8);
|
||||||
|
|
||||||
m_psPassword = new ProtectedString(true, 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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
@@ -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,11 +131,9 @@ namespace ModernKeePassLib.Keys
|
|||||||
|
|
||||||
private static byte[] CreateUserKey()
|
private static byte[] CreateUserKey()
|
||||||
{
|
{
|
||||||
byte[] pbKey = null;
|
#if KeePassLibSD
|
||||||
|
return null;
|
||||||
#if !KeePassLibSD
|
#else
|
||||||
try
|
|
||||||
{
|
|
||||||
string strFilePath = GetUserKeyFilePath(true);
|
string strFilePath = GetUserKeyFilePath(true);
|
||||||
|
|
||||||
byte[] pbRandomKey = CryptoRandom.Instance.GetRandomBytes(64);
|
byte[] pbRandomKey = CryptoRandom.Instance.GetRandomBytes(64);
|
||||||
@@ -138,15 +142,12 @@ namespace ModernKeePassLib.Keys
|
|||||||
|
|
||||||
File.WriteAllBytes(strFilePath, pbProtectedKey);
|
File.WriteAllBytes(strFilePath, pbProtectedKey);
|
||||||
|
|
||||||
Array.Clear(pbProtectedKey, 0, pbProtectedKey.Length);
|
byte[] pbKey = LoadUserKey(true);
|
||||||
Array.Clear(pbRandomKey, 0, pbRandomKey.Length);
|
Debug.Assert(MemUtil.ArraysEqual(pbKey, pbRandomKey));
|
||||||
|
|
||||||
pbKey = LoadUserKey(true);
|
|
||||||
}
|
|
||||||
catch(Exception) { pbKey = null; }
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
MemUtil.ZeroByteArray(pbRandomKey);
|
||||||
return pbKey;
|
return pbKey;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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>
|
||||||
@@ -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" />
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
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
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
foreach(PwGroup pg in m_listGroups)
|
||||||
|
{
|
||||||
if(groupHandler != null)
|
if(groupHandler != null)
|
||||||
{
|
|
||||||
foreach(PwGroup pg in m_listGroups)
|
|
||||||
{
|
{
|
||||||
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);
|
||||||
}
|
|
||||||
}
|
|
||||||
else pg = pgNew;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(pg != null) listStorage.Add(pg.Entries);
|
lCand = lRem;
|
||||||
sp.SearchString = strFullSearch; // Restore
|
}
|
||||||
|
else lCand = l;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool SearchEntriesSingle(SearchParameters spIn,
|
Debug.Assert(lResults.UCount == 0);
|
||||||
PwObjectList<PwEntry> listStorage, IStatusLogger slStatus,
|
lResults.Clear();
|
||||||
ref ulong uCurEntries, ulong uTotalEntries)
|
lResults.Add(lCand);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool SearchEntriesSingle(PwObjectList<PwEntry> lSource,
|
||||||
|
SearchParameters sp, PwObjectList<PwEntry> lResults,
|
||||||
|
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;
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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; }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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];
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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,19 +67,17 @@ 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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -125,62 +125,75 @@ 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();
|
||||||
if (str == null)
|
sr.Dispose();
|
||||||
{
|
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; }
|
||||||
{
|
|
||||||
Debug.Assert(false);
|
|
||||||
}
|
|
||||||
return new LockFileInfo(v[1], v[2], v[3], v[4], v[5]);
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Throws on error
|
// Throws on error
|
||||||
public static LockFileInfo Create(IOConnectionInfo iocLockFile)
|
public static LockFileInfo Create(IOConnectionInfo iocLockFile)
|
||||||
|
{
|
||||||
|
LockFileInfo lfi;
|
||||||
|
Stream s = null;
|
||||||
|
try
|
||||||
{
|
{
|
||||||
byte[] pbID = CryptoRandom.Instance.GetRandomBytes(16);
|
byte[] pbID = CryptoRandom.Instance.GetRandomBytes(16);
|
||||||
string strTime = TimeUtil.SerializeUtc(DateTime.Now);
|
string strTime = TimeUtil.SerializeUtc(DateTime.UtcNow);
|
||||||
|
|
||||||
var lfi = new LockFileInfo(Convert.ToBase64String(pbID), strTime,
|
lfi = new LockFileInfo(Convert.ToBase64String(pbID), strTime,
|
||||||
|
#if KeePassUAP
|
||||||
|
EnvironmentExt.UserName, EnvironmentExt.MachineName,
|
||||||
|
EnvironmentExt.UserDomainName);
|
||||||
|
#elif ModernKeePassLib|| KeePassLibSD
|
||||||
string.Empty, string.Empty, string.Empty);
|
string.Empty, string.Empty, string.Empty);
|
||||||
|
#else
|
||||||
|
Environment.UserName, Environment.MachineName,
|
||||||
|
Environment.UserDomainName);
|
||||||
|
#endif
|
||||||
|
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
|
#if !KeePassLibSD
|
||||||
sb.AppendLine(LockFileHeader);
|
sb.AppendLine(LockFileHeader);
|
||||||
sb.AppendLine(lfi.ID);
|
sb.AppendLine(lfi.ID);
|
||||||
sb.AppendLine(strTime);
|
sb.AppendLine(strTime);
|
||||||
sb.AppendLine(lfi.UserName);
|
sb.AppendLine(lfi.UserName);
|
||||||
sb.AppendLine(lfi.Machine);
|
sb.AppendLine(lfi.Machine);
|
||||||
sb.AppendLine(lfi.Domain);
|
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
|
||||||
|
|
||||||
using (var s = IOConnection.OpenWrite(iocLockFile))
|
|
||||||
{
|
|
||||||
byte[] pbFile = StrUtil.Utf8.GetBytes(sb.ToString());
|
byte[] pbFile = StrUtil.Utf8.GetBytes(sb.ToString());
|
||||||
|
|
||||||
|
s = IOConnection.OpenWrite(iocLockFile);
|
||||||
if(s == null) throw new IOException(iocLockFile.GetDisplayName());
|
if(s == null) throw new IOException(iocLockFile.GetDisplayName());
|
||||||
s.WriteAsync(pbFile, 0, pbFile.Length).GetAwaiter().GetResult();
|
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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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_bWriting) // Reading mode
|
||||||
if(m_sBaseStream != null)
|
|
||||||
{
|
{
|
||||||
if(m_bWriting == false) // Reading mode
|
m_brInput.Dispose();
|
||||||
{
|
|
||||||
try { m_brInput.Dispose(); } catch { }
|
|
||||||
|
|
||||||
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,26 +246,13 @@ namespace ModernKeePassLib.Serialization
|
|||||||
|
|
||||||
if(m_bVerify)
|
if(m_bVerify)
|
||||||
{
|
{
|
||||||
#if ModernKeePassLib
|
byte[] pbComputedHash = CryptoUtil.HashSha256(m_pbBuffer);
|
||||||
/*var sha256 = WinRTCrypto.HashAlgorithmProvider.OpenAlgorithm(HashAlgorithm.Sha256);
|
|
||||||
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))
|
if((pbComputedHash == null) || (pbComputedHash.Length != 32))
|
||||||
throw new InvalidOperationException();
|
throw new InvalidOperationException();
|
||||||
|
|
||||||
for(int iHashPos = 0; iHashPos < 32; ++iHashPos)
|
if(!MemUtil.ArraysEqual(pbStoredHash, pbComputedHash))
|
||||||
{
|
|
||||||
if(pbStoredHash[iHashPos] != pbComputedHash[iHashPos])
|
|
||||||
throw new InvalidDataException();
|
throw new InvalidDataException();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -297,37 +279,24 @@ 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();
|
||||||
#if !KeePassLibSD
|
// byte[] pbHash;
|
||||||
byte[] pbHash = sha256.ComputeHash(m_pbBuffer, 0, m_nBufferPos);
|
// if(m_nBufferPos == m_pbBuffer.Length)
|
||||||
#else
|
// pbHash = sha256.ComputeHash(m_pbBuffer);
|
||||||
byte[] pbHash;
|
// else
|
||||||
if(m_nBufferPos == m_pbBuffer.Length)
|
// {
|
||||||
pbHash = sha256.ComputeHash(m_pbBuffer);
|
// byte[] pbData = new byte[m_nBufferPos];
|
||||||
else
|
// Array.Copy(m_pbBuffer, 0, pbData, 0, m_nBufferPos);
|
||||||
{
|
// pbHash = sha256.ComputeHash(pbData);
|
||||||
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);
|
m_bwOutput.Write(pbHash);
|
||||||
}
|
}
|
||||||
|
|||||||
356
ModernKeePassLib/Serialization/HmacBlockStream.cs
Normal file
356
ModernKeePassLib/Serialization/HmacBlockStream.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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,54 +383,107 @@ 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(prx == null) return; // No assert
|
||||||
|
|
||||||
|
string strUserName = m_strProxyUserName;
|
||||||
|
string strPassword = m_strProxyPassword;
|
||||||
|
|
||||||
|
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)
|
if(m_bSslCertsAcceptInvalid)
|
||||||
ServicePointManager.ServerCertificateValidationCallback =
|
ServicePointManager.ServerCertificateValidationCallback =
|
||||||
@@ -375,12 +491,55 @@ namespace ModernKeePassLib.Serialization
|
|||||||
else
|
else
|
||||||
ServicePointManager.ServerCertificateValidationCallback = null;
|
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);
|
||||||
@@ -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);
|
||||||
|
|
||||||
@@ -535,12 +691,12 @@ 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
|
||||||
@@ -550,11 +706,10 @@ namespace ModernKeePassLib.Serialization
|
|||||||
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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
@@ -326,7 +357,7 @@ namespace ModernKeePassLib.Serialization
|
|||||||
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)
|
||||||
|
|||||||
192
ModernKeePassLib/Serialization/IocProperties.cs
Normal file
192
ModernKeePassLib/Serialization/IocProperties.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
99
ModernKeePassLib/Serialization/IocPropertyInfo.cs
Normal file
99
ModernKeePassLib/Serialization/IocPropertyInfo.cs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
123
ModernKeePassLib/Serialization/IocPropertyInfoPool.cs
Normal file
123
ModernKeePassLib/Serialization/IocPropertyInfoPool.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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)
|
||||||
@@ -781,16 +916,45 @@ namespace ModernKeePassLib.Serialization
|
|||||||
}
|
}
|
||||||
|
|
||||||
private DateTime ReadTime(XmlReader xr)
|
private DateTime ReadTime(XmlReader 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);
|
||||||
|
|
||||||
|
byte[] pb = ReadBase64(xr, false);
|
||||||
|
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);
|
string str = ReadString(xr);
|
||||||
|
|
||||||
DateTime dt;
|
DateTime dt;
|
||||||
if(TimeUtil.TryDeserializeUtc(str, out dt)) return 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);
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
|
ComputeKeys(out pbCipherKey, cbEncKey, out pbHmacKey64);
|
||||||
|
|
||||||
|
string strIncomplete = KLRes.FileHeaderCorrupted + " " +
|
||||||
|
KLRes.FileIncomplete;
|
||||||
|
|
||||||
|
Stream sPlain;
|
||||||
|
if(m_uFileVersion < FileVersion32_4)
|
||||||
|
{
|
||||||
|
Stream sDecrypted = EncryptStream(sHashing, iCipher,
|
||||||
|
pbCipherKey, cbEncIV, false);
|
||||||
|
if((sDecrypted == null) || (sDecrypted == sHashing))
|
||||||
throw new SecurityException(KLRes.CryptoStreamFailed);
|
throw new SecurityException(KLRes.CryptoStreamFailed);
|
||||||
|
lStreams.Add(sDecrypted);
|
||||||
|
|
||||||
brDecrypted = new BinaryReaderEx(sDecrypted, encNoBom, KLRes.FileCorrupted);
|
BinaryReaderEx brDecrypted = new BinaryReaderEx(sDecrypted,
|
||||||
|
encNoBom, strIncomplete);
|
||||||
byte[] pbStoredStartBytes = brDecrypted.ReadBytes(32);
|
byte[] pbStoredStartBytes = brDecrypted.ReadBytes(32);
|
||||||
|
|
||||||
if((m_pbStreamStartBytes == null) || (m_pbStreamStartBytes.Length != 32))
|
if((m_pbStreamStartBytes == null) || (m_pbStreamStartBytes.Length != 32))
|
||||||
throw new InvalidDataException();
|
throw new EndOfStreamException(strIncomplete);
|
||||||
|
if(!MemUtil.ArraysEqual(pbStoredStartBytes, m_pbStreamStartBytes))
|
||||||
for(int iStart = 0; iStart < 32; ++iStart)
|
|
||||||
{
|
|
||||||
if(pbStoredStartBytes[iStart] != m_pbStreamStartBytes[iStart])
|
|
||||||
throw new InvalidCompositeKeyException();
|
throw new InvalidCompositeKeyException();
|
||||||
}
|
|
||||||
|
|
||||||
Stream sHashed = new HashedBlockStream(sDecrypted, false, 0,
|
sPlain = new HashedBlockStream(sDecrypted, false, 0, !m_bRepairMode);
|
||||||
!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;
|
|
||||||
}
|
|
||||||
else if(kdbFormat == KdbxFormat.PlainXml)
|
|
||||||
readerStream = hashedStream;
|
|
||||||
else { Debug.Assert(false); throw new FormatException("KdbFormat"); }
|
|
||||||
|
|
||||||
if(kdbFormat != KdbxFormat.PlainXml) // Is an encrypted format
|
|
||||||
{
|
{
|
||||||
if(m_pbProtectedStreamKey == null)
|
sXml = new GZipStream(sPlain, CompressionMode.Decompress);
|
||||||
|
lStreams.Add(sXml);
|
||||||
|
}
|
||||||
|
else sXml = sPlain;
|
||||||
|
|
||||||
|
if(m_uFileVersion >= FileVersion32_4)
|
||||||
|
LoadInnerHeader(sXml); // Binary header before XML
|
||||||
|
}
|
||||||
|
else if(fmt == KdbxFormat.PlainXml)
|
||||||
|
sXml = sHashing;
|
||||||
|
else { Debug.Assert(false); throw new ArgumentOutOfRangeException("fmt"); }
|
||||||
|
|
||||||
|
if(fmt == KdbxFormat.Default)
|
||||||
|
{
|
||||||
|
if(m_pbInnerRandomStreamKey == null)
|
||||||
{
|
{
|
||||||
Debug.Assert(false);
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CommonCleanUpRead(Stream sSource, HashingStreamEx hashedStream)
|
#if KDBX_BENCHMARK
|
||||||
{
|
swTime.Stop();
|
||||||
hashedStream.Dispose();
|
MessageService.ShowInfo("Loading KDBX took " +
|
||||||
m_pbHashOfFileOnDisk = hashedStream.Hash;
|
swTime.ElapsedMilliseconds.ToString() + " ms.");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
sSource.Dispose();
|
private void CommonCleanUpRead(List<Stream> lStreams, HashingStreamEx sHashing)
|
||||||
|
{
|
||||||
|
CloseStreams(lStreams);
|
||||||
|
|
||||||
|
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,28 +290,20 @@ 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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,96 +76,189 @@ 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(m_uFileVersion < FileVersion32_4)
|
||||||
|
m_pbStreamStartBytes = cr.GetRandomBytes(32);
|
||||||
|
|
||||||
|
Stream sXml;
|
||||||
|
if(m_format == KdbxFormat.Default)
|
||||||
|
{
|
||||||
|
byte[] pbHeader = GenerateHeader();
|
||||||
|
m_pbHashOfHeader = CryptoUtil.HashSha256(pbHeader);
|
||||||
|
|
||||||
|
MemUtil.Write(sHashing, pbHeader);
|
||||||
|
sHashing.Flush();
|
||||||
|
|
||||||
|
ComputeKeys(out pbCipherKey, cbEncKey, out pbHmacKey64);
|
||||||
|
|
||||||
|
Stream sPlain;
|
||||||
|
if(m_uFileVersion < FileVersion32_4)
|
||||||
|
{
|
||||||
|
Stream sEncrypted = EncryptStream(sHashing, iCipher,
|
||||||
|
pbCipherKey, cbEncIV, true);
|
||||||
|
if((sEncrypted == null) || (sEncrypted == sHashing))
|
||||||
throw new SecurityException(KLRes.CryptoStreamFailed);
|
throw new SecurityException(KLRes.CryptoStreamFailed);
|
||||||
|
lStreams.Add(sEncrypted);
|
||||||
|
|
||||||
sEncrypted.Write(m_pbStreamStartBytes, 0, m_pbStreamStartBytes.Length);
|
MemUtil.Write(sEncrypted, m_pbStreamStartBytes);
|
||||||
|
|
||||||
Stream sHashed = new HashedBlockStream(sEncrypted, true);
|
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
|
||||||
}
|
|
||||||
|
|
||||||
private void CommonCleanUpWrite(Stream sSaveTo, HashingStreamEx hashedStream)
|
|
||||||
{
|
{
|
||||||
hashedStream.Dispose();
|
if(pbCipherKey != null) MemUtil.ZeroByteArray(pbCipherKey);
|
||||||
m_pbHashOfFileOnDisk = hashedStream.Hash;
|
if(pbHmacKey64 != null) MemUtil.ZeroByteArray(pbHmacKey64);
|
||||||
|
|
||||||
sSaveTo.Dispose();
|
CommonCleanUpWrite(lStreams, sHashing);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CommonCleanUpWrite(List<Stream> lStreams, HashingStreamEx sHashing)
|
||||||
|
{
|
||||||
|
CloseStreams(lStreams);
|
||||||
|
|
||||||
|
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(FileSignature1));
|
||||||
MemUtil.Write(ms, MemUtil.UInt32ToBytes(FileSignature2));
|
MemUtil.Write(ms, MemUtil.UInt32ToBytes(FileSignature2));
|
||||||
MemUtil.Write(ms, MemUtil.UInt32ToBytes(FileVersion32));
|
MemUtil.Write(ms, MemUtil.UInt32ToBytes(m_uFileVersion));
|
||||||
|
|
||||||
WriteHeaderField(ms, KdbxHeaderFieldID.CipherID,
|
WriteHeaderField(ms, KdbxHeaderFieldID.CipherID,
|
||||||
m_pwDatabase.DataCipherUuid.UuidBytes);
|
m_pwDatabase.DataCipherUuid.UuidBytes);
|
||||||
@@ -174,113 +268,132 @@ namespace ModernKeePassLib.Serialization
|
|||||||
MemUtil.UInt32ToBytes((uint)nCprID));
|
MemUtil.UInt32ToBytes((uint)nCprID));
|
||||||
|
|
||||||
WriteHeaderField(ms, KdbxHeaderFieldID.MasterSeed, m_pbMasterSeed);
|
WriteHeaderField(ms, KdbxHeaderFieldID.MasterSeed, m_pbMasterSeed);
|
||||||
WriteHeaderField(ms, KdbxHeaderFieldID.TransformSeed, m_pbTransformSeed);
|
|
||||||
|
if(m_uFileVersion < FileVersion32_4)
|
||||||
|
{
|
||||||
|
Debug.Assert(m_pwDatabase.KdfParameters.KdfUuid.Equals(
|
||||||
|
(new AesKdf()).Uuid));
|
||||||
|
WriteHeaderField(ms, KdbxHeaderFieldID.TransformSeed,
|
||||||
|
m_pwDatabase.KdfParameters.GetByteArray(AesKdf.ParamSeed));
|
||||||
WriteHeaderField(ms, KdbxHeaderFieldID.TransformRounds,
|
WriteHeaderField(ms, KdbxHeaderFieldID.TransformRounds,
|
||||||
MemUtil.UInt64ToBytes(m_pwDatabase.KeyEncryptionRounds));
|
MemUtil.UInt64ToBytes(m_pwDatabase.KdfParameters.GetUInt64(
|
||||||
|
AesKdf.ParamRounds, PwDefs.DefaultKeyEncryptionRounds)));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
WriteHeaderField(ms, KdbxHeaderFieldID.KdfParameters,
|
||||||
|
KdfParameters.SerializeExt(m_pwDatabase.KdfParameters));
|
||||||
|
|
||||||
|
if(m_pbEncryptionIV.Length > 0)
|
||||||
WriteHeaderField(ms, KdbxHeaderFieldID.EncryptionIV, m_pbEncryptionIV);
|
WriteHeaderField(ms, KdbxHeaderFieldID.EncryptionIV, m_pbEncryptionIV);
|
||||||
WriteHeaderField(ms, KdbxHeaderFieldID.ProtectedStreamKey, m_pbProtectedStreamKey);
|
|
||||||
WriteHeaderField(ms, KdbxHeaderFieldID.StreamStartBytes, m_pbStreamStartBytes);
|
if(m_uFileVersion < FileVersion32_4)
|
||||||
|
{
|
||||||
|
WriteHeaderField(ms, KdbxHeaderFieldID.InnerRandomStreamKey,
|
||||||
|
m_pbInnerRandomStreamKey);
|
||||||
|
|
||||||
|
WriteHeaderField(ms, KdbxHeaderFieldID.StreamStartBytes,
|
||||||
|
m_pbStreamStartBytes);
|
||||||
|
|
||||||
int nIrsID = (int)m_craInnerRandomStream;
|
int nIrsID = (int)m_craInnerRandomStream;
|
||||||
WriteHeaderField(ms, KdbxHeaderFieldID.InnerRandomStreamID,
|
WriteHeaderField(ms, KdbxHeaderFieldID.InnerRandomStreamID,
|
||||||
MemUtil.UInt32ToBytes((uint) nIrsID));
|
MemUtil.Int32ToBytes(nIrsID));
|
||||||
|
|
||||||
WriteHeaderField(ms, KdbxHeaderFieldID.EndOfHeader, new byte[]
|
|
||||||
{
|
|
||||||
(byte) '\r', (byte) '\n', (byte) '\r', (byte) '\n'
|
|
||||||
});
|
|
||||||
|
|
||||||
byte[] pbHeader = ms.ToArray();
|
|
||||||
|
|
||||||
#if ModernKeePassLib
|
|
||||||
/*var sha256 = WinRTCrypto.HashAlgorithmProvider.OpenAlgorithm(HashAlgorithm.Sha256);
|
|
||||||
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
|
|
||||||
|
|
||||||
s.Write(pbHeader, 0, pbHeader.Length);
|
|
||||||
s.Flush();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void WriteHeaderField(Stream s, KdbxHeaderFieldID kdbID,
|
// Write public custom data only when there is at least one item,
|
||||||
|
// 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 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));
|
|
||||||
|
|
||||||
if(uLength > 0) s.Write(pbData, 0, pbData.Length);
|
|
||||||
}
|
|
||||||
else MemUtil.Write(s, MemUtil.UInt16ToBytes((ushort)0));
|
|
||||||
}
|
|
||||||
|
|
||||||
private Stream AttachStreamEncryptor(Stream s)
|
|
||||||
{
|
{
|
||||||
using (var ms = new MemoryStream())
|
Debug.Assert(false);
|
||||||
|
throw new ArgumentOutOfRangeException("pbData");
|
||||||
|
}
|
||||||
|
|
||||||
|
MemUtil.Write(s, MemUtil.UInt16ToBytes((ushort)cb));
|
||||||
|
}
|
||||||
|
else MemUtil.Write(s, MemUtil.Int32ToBytes(cb));
|
||||||
|
|
||||||
|
MemUtil.Write(s, pb);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WriteInnerHeader(Stream s)
|
||||||
{
|
{
|
||||||
Debug.Assert(m_pbMasterSeed != null);
|
int nIrsID = (int)m_craInnerRandomStream;
|
||||||
Debug.Assert(m_pbMasterSeed.Length == 32);
|
WriteInnerHeaderField(s, KdbxInnerHeaderFieldID.InnerRandomStreamID,
|
||||||
ms.Write(m_pbMasterSeed, 0, 32);
|
MemUtil.Int32ToBytes(nIrsID), null);
|
||||||
|
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void WriteDocument(PwGroup pgDataSource)
|
WriteInnerHeaderField(s, KdbxInnerHeaderFieldID.EndOfHeader,
|
||||||
|
null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
if((m_format != KdbxFormat.Default) || (m_uFileVersion < FileVersion32_4))
|
||||||
WriteBinPool();
|
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);
|
||||||
|
|||||||
@@ -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 void BinPoolAdd(ProtectedBinary pb)
|
private ICipherEngine GetCipher(out int cbEncKey, out int cbEncIV)
|
||||||
{
|
{
|
||||||
if(pb == null) { Debug.Assert(false); return; }
|
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() + ".");
|
||||||
|
|
||||||
if(BinPoolFind(pb) != null) return; // Exists already
|
ICipherEngine2 iCipher2 = (iCipher as ICipherEngine2);
|
||||||
|
if(iCipher2 != null)
|
||||||
|
{
|
||||||
|
cbEncKey = iCipher2.KeyLength;
|
||||||
|
if(cbEncKey < 0) throw new InvalidOperationException("EncKey.Length");
|
||||||
|
|
||||||
m_dictBinPool.Add(m_dictBinPool.Count.ToString(
|
cbEncIV = iCipher2.IVLength;
|
||||||
NumberFormatInfo.InvariantInfo), pb);
|
if(cbEncIV < 0) throw new InvalidOperationException("EncIV.Length");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cbEncKey = 32;
|
||||||
|
cbEncIV = 16;
|
||||||
}
|
}
|
||||||
|
|
||||||
private string BinPoolFind(ProtectedBinary pb)
|
return iCipher;
|
||||||
{
|
|
||||||
if(pb == null) { Debug.Assert(false); return null; }
|
|
||||||
|
|
||||||
foreach(KeyValuePair<string, ProtectedBinary> kvp in m_dictBinPool)
|
|
||||||
{
|
|
||||||
if(pb.Equals(kvp.Value)) return kvp.Key;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
private Stream EncryptStream(Stream s, ICipherEngine iCipher,
|
||||||
|
byte[] pbKey, int cbIV, bool bEncrypt)
|
||||||
|
{
|
||||||
|
byte[] pbIV = (m_pbEncryptionIV ?? MemUtil.EmptyByteArray);
|
||||||
|
if(pbIV.Length != cbIV)
|
||||||
|
{
|
||||||
|
Debug.Assert(false);
|
||||||
|
throw new Exception(KLRes.FileCorrupted);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ProtectedBinary BinPoolGet(string strKey)
|
if(bEncrypt)
|
||||||
|
return iCipher.EncryptStream(s, pbKey, pbIV);
|
||||||
|
return iCipher.DecryptStream(s, pbKey, pbIV);
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] ComputeHeaderHmac(byte[] pbHeader, byte[] pbKey)
|
||||||
{
|
{
|
||||||
if(strKey == null) { Debug.Assert(false); return null; }
|
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);
|
||||||
|
|
||||||
ProtectedBinary pb;
|
return pbHeaderHmac;
|
||||||
if(m_dictBinPool.TryGetValue(strKey, out pb)) return pb;
|
}
|
||||||
|
|
||||||
return null;
|
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,
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
if((c is GroupBox) || (c is Panel)) RtlMoveChildControls(c);
|
{
|
||||||
|
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(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)
|
||||||
|
{
|
||||||
|
DockStyle ds = c.Dock;
|
||||||
|
if(ds == DockStyle.Left)
|
||||||
|
c.Dock = DockStyle.Right;
|
||||||
|
else if(ds == DockStyle.Right)
|
||||||
|
c.Dock = DockStyle.Left;
|
||||||
|
else
|
||||||
{
|
{
|
||||||
Point ptCur = c.Location;
|
Point ptCur = c.Location;
|
||||||
c.Location = new Point(nParentWidth - c.Size.Width - ptCur.X, ptCur.Y);
|
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)
|
||||||
{
|
{
|
||||||
|
if(tsi == null) { Debug.Assert(false); continue; }
|
||||||
|
|
||||||
|
if(Array.IndexOf<string>(g_vRtlMirrorItemNames, tsi.Name) >= 0)
|
||||||
tsi.RightToLeftAutoMirrorImage = true;
|
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)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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(':', '-');
|
||||||
|
|||||||
@@ -28,23 +28,57 @@ 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)
|
||||||
|
private sealed class GfxImage
|
||||||
|
{
|
||||||
|
public byte[] Data;
|
||||||
|
|
||||||
|
public int Width;
|
||||||
|
public int Height;
|
||||||
|
|
||||||
|
public GfxImage(byte[] pbData, int w, int h)
|
||||||
|
{
|
||||||
|
this.Data = pbData;
|
||||||
|
this.Width = w;
|
||||||
|
this.Height = h;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
// For debugger display
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return (this.Width.ToString() + "x" + this.Height.ToString());
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if KeePassUAP
|
||||||
public static Image LoadImage(byte[] pb)
|
public static Image LoadImage(byte[] pb)
|
||||||
{
|
{
|
||||||
|
if(pb == null) throw new ArgumentNullException("pb");
|
||||||
|
|
||||||
MemoryStream ms = new MemoryStream(pb, false);
|
MemoryStream ms = new MemoryStream(pb, false);
|
||||||
try { return Image.FromStream(ms); }
|
try { return Image.FromStream(ms); }
|
||||||
finally { ms.Close(); }
|
finally { ms.Close(); }
|
||||||
}
|
}
|
||||||
#elif ModernKeePassLib
|
#elif ModernKeePassLib
|
||||||
public static IBitmap LoadImage(byte[] pb)
|
public static async Task<IBitmap> LoadImage(byte[] pb)
|
||||||
{
|
{
|
||||||
using (var ms = new MemoryStream(pb, false)) {
|
return await ScaleImage(pb, null, null);
|
||||||
return BitmapLoader.Current.Load(ms, null, null).Result;
|
}
|
||||||
|
|
||||||
|
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
|
#else
|
||||||
@@ -52,14 +86,21 @@ namespace ModernKeePassLib.Utility
|
|||||||
{
|
{
|
||||||
if(pb == null) throw new ArgumentNullException("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 = ExtractBestImageFromIco(pb);
|
||||||
|
if(imgIco != null) return imgIco;
|
||||||
|
}
|
||||||
|
catch(Exception) { Debug.Assert(false); }
|
||||||
|
#endif
|
||||||
|
|
||||||
MemoryStream ms = new MemoryStream(pb, false);
|
MemoryStream ms = new MemoryStream(pb, false);
|
||||||
try { return LoadImagePriv(ms); }
|
try { return LoadImagePriv(ms); }
|
||||||
catch(Exception)
|
|
||||||
{
|
|
||||||
Image imgIco = TryLoadIco(pb);
|
|
||||||
if(imgIco != null) return imgIco;
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
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,16 +145,312 @@ 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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
{
|
||||||
|
using(MemoryStream msCompressed = new MemoryStream())
|
||||||
|
{
|
||||||
|
using(GZipStream gz = new GZipStream(msCompressed,
|
||||||
|
CompressionMode.Compress))
|
||||||
|
{
|
||||||
MemUtil.CopyStream(msSource, gz);
|
MemUtil.CopyStream(msSource, gz);
|
||||||
gz.Dispose();
|
}
|
||||||
msSource.Dispose();
|
|
||||||
|
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();
|
{
|
||||||
|
using(MemoryStream msCompressed = new MemoryStream(pbCompressed, false))
|
||||||
|
{
|
||||||
|
using(GZipStream gz = new GZipStream(msCompressed,
|
||||||
|
CompressionMode.Decompress))
|
||||||
|
{
|
||||||
MemUtil.CopyStream(gz, msData);
|
MemUtil.CopyStream(gz, msData);
|
||||||
gz.Dispose();
|
}
|
||||||
msCompressed.Dispose();
|
}
|
||||||
|
|
||||||
|
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
Reference in New Issue
Block a user