mirror of
https://github.com/wismna/ModernKeePass.git
synced 2025-10-03 23:50:18 -04:00
WIP Update lib to 2.37
This commit is contained in:
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
|
||||
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
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -41,12 +41,17 @@ namespace ModernKeePassLib.Cryptography.Cipher
|
||||
{
|
||||
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.AddCipher(new StandardAesEngine());
|
||||
m_poolGlobal = cp;
|
||||
}
|
||||
|
||||
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
|
||||
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
|
||||
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>
|
||||
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
|
||||
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
|
||||
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
|
||||
*/
|
||||
|
||||
// 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.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
|
||||
using ModernKeePassLib.Utility;
|
||||
|
||||
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 byte[] m_output = new byte[64];
|
||||
private int m_outputPos = 64;
|
||||
|
||||
private static readonly uint[] m_sigma = new uint[4] {
|
||||
private static readonly uint[] g_sigma = new uint[4] {
|
||||
0x61707865, 0x3320646E, 0x79622D32, 0x6B206574
|
||||
};
|
||||
|
||||
public Salsa20Cipher(byte[] pbKey32, byte[] pbIV8)
|
||||
public override int BlockSize
|
||||
{
|
||||
KeySetup(pbKey32);
|
||||
IvSetup(pbIV8);
|
||||
get { return 64; }
|
||||
}
|
||||
|
||||
~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);
|
||||
GC.SuppressFinalize(this);
|
||||
if(bDisposing)
|
||||
{
|
||||
MemUtil.ZeroArray<uint>(m_s);
|
||||
MemUtil.ZeroArray<uint>(m_x);
|
||||
}
|
||||
|
||||
base.Dispose(bDisposing);
|
||||
}
|
||||
|
||||
private void Dispose(bool bDisposing)
|
||||
protected override void NextBlock(byte[] pBlock)
|
||||
{
|
||||
// Clear sensitive data
|
||||
Array.Clear(m_state, 0, m_state.Length);
|
||||
Array.Clear(m_x, 0, m_x.Length);
|
||||
}
|
||||
if(pBlock == null) throw new ArgumentNullException("pBlock");
|
||||
if(pBlock.Length != 64) throw new ArgumentOutOfRangeException("pBlock");
|
||||
|
||||
private void NextOutput()
|
||||
{
|
||||
uint[] x = m_x; // Local alias for working buffer
|
||||
|
||||
// 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();
|
||||
|
||||
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
|
||||
{
|
||||
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[ 8] ^= Rotl32(x[ 4] + x[ 0], 9);
|
||||
x[12] ^= Rotl32(x[ 8] + x[ 4], 13);
|
||||
x[ 0] ^= Rotl32(x[12] + x[ 8], 18);
|
||||
x[ 9] ^= Rotl32(x[ 5] + x[ 1], 7);
|
||||
x[13] ^= Rotl32(x[ 9] + x[ 5], 9);
|
||||
x[ 1] ^= Rotl32(x[13] + x[ 9], 13);
|
||||
x[ 5] ^= Rotl32(x[ 1] + x[13], 18);
|
||||
x[14] ^= Rotl32(x[10] + x[ 6], 7);
|
||||
x[ 2] ^= Rotl32(x[14] + x[10], 9);
|
||||
x[ 6] ^= Rotl32(x[ 2] + x[14], 13);
|
||||
x[10] ^= Rotl32(x[ 6] + x[ 2], 18);
|
||||
x[ 3] ^= Rotl32(x[15] + x[11], 7);
|
||||
x[ 7] ^= Rotl32(x[ 3] + x[15], 9);
|
||||
x[11] ^= Rotl32(x[ 7] + x[ 3], 13);
|
||||
x[15] ^= Rotl32(x[11] + x[ 7], 18);
|
||||
x[ 1] ^= Rotl32(x[ 0] + x[ 3], 7);
|
||||
x[ 2] ^= Rotl32(x[ 1] + x[ 0], 9);
|
||||
x[ 3] ^= Rotl32(x[ 2] + x[ 1], 13);
|
||||
x[ 0] ^= Rotl32(x[ 3] + x[ 2], 18);
|
||||
x[ 6] ^= Rotl32(x[ 5] + x[ 4], 7);
|
||||
x[ 7] ^= Rotl32(x[ 6] + x[ 5], 9);
|
||||
x[ 4] ^= Rotl32(x[ 7] + x[ 6], 13);
|
||||
x[ 5] ^= Rotl32(x[ 4] + x[ 7], 18);
|
||||
x[11] ^= Rotl32(x[10] + x[ 9], 7);
|
||||
x[ 8] ^= Rotl32(x[11] + x[10], 9);
|
||||
x[ 9] ^= Rotl32(x[ 8] + x[11], 13);
|
||||
x[10] ^= Rotl32(x[ 9] + x[ 8], 18);
|
||||
x[12] ^= Rotl32(x[15] + x[14], 7);
|
||||
x[13] ^= Rotl32(x[12] + x[15], 9);
|
||||
x[14] ^= Rotl32(x[13] + x[12], 13);
|
||||
x[15] ^= Rotl32(x[14] + x[13], 18);
|
||||
x[ 4] ^= MemUtil.RotateLeft32(x[ 0] + x[12], 7);
|
||||
x[ 8] ^= MemUtil.RotateLeft32(x[ 4] + x[ 0], 9);
|
||||
x[12] ^= MemUtil.RotateLeft32(x[ 8] + x[ 4], 13);
|
||||
x[ 0] ^= MemUtil.RotateLeft32(x[12] + x[ 8], 18);
|
||||
|
||||
x[ 9] ^= MemUtil.RotateLeft32(x[ 5] + x[ 1], 7);
|
||||
x[13] ^= MemUtil.RotateLeft32(x[ 9] + x[ 5], 9);
|
||||
x[ 1] ^= MemUtil.RotateLeft32(x[13] + x[ 9], 13);
|
||||
x[ 5] ^= MemUtil.RotateLeft32(x[ 1] + x[13], 18);
|
||||
|
||||
x[14] ^= MemUtil.RotateLeft32(x[10] + x[ 6], 7);
|
||||
x[ 2] ^= MemUtil.RotateLeft32(x[14] + x[10], 9);
|
||||
x[ 6] ^= MemUtil.RotateLeft32(x[ 2] + x[14], 13);
|
||||
x[10] ^= MemUtil.RotateLeft32(x[ 6] + x[ 2], 18);
|
||||
|
||||
x[ 3] ^= MemUtil.RotateLeft32(x[15] + x[11], 7);
|
||||
x[ 7] ^= MemUtil.RotateLeft32(x[ 3] + x[15], 9);
|
||||
x[11] ^= MemUtil.RotateLeft32(x[ 7] + x[ 3], 13);
|
||||
x[15] ^= MemUtil.RotateLeft32(x[11] + x[ 7], 18);
|
||||
|
||||
x[ 1] ^= MemUtil.RotateLeft32(x[ 0] + x[ 3], 7);
|
||||
x[ 2] ^= MemUtil.RotateLeft32(x[ 1] + x[ 0], 9);
|
||||
x[ 3] ^= MemUtil.RotateLeft32(x[ 2] + x[ 1], 13);
|
||||
x[ 0] ^= MemUtil.RotateLeft32(x[ 3] + x[ 2], 18);
|
||||
|
||||
x[ 6] ^= MemUtil.RotateLeft32(x[ 5] + x[ 4], 7);
|
||||
x[ 7] ^= MemUtil.RotateLeft32(x[ 6] + x[ 5], 9);
|
||||
x[ 4] ^= MemUtil.RotateLeft32(x[ 7] + x[ 6], 13);
|
||||
x[ 5] ^= MemUtil.RotateLeft32(x[ 4] + x[ 7], 18);
|
||||
|
||||
x[11] ^= MemUtil.RotateLeft32(x[10] + x[ 9], 7);
|
||||
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)
|
||||
x[i] += m_state[i];
|
||||
for(int i = 0; i < 16; ++i) x[i] += s[i];
|
||||
|
||||
for(int i = 0; i < 16; ++i)
|
||||
{
|
||||
m_output[i << 2] = (byte)x[i];
|
||||
m_output[(i << 2) + 1] = (byte)(x[i] >> 8);
|
||||
m_output[(i << 2) + 2] = (byte)(x[i] >> 16);
|
||||
m_output[(i << 2) + 3] = (byte)(x[i] >> 24);
|
||||
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);
|
||||
}
|
||||
|
||||
m_outputPos = 0;
|
||||
++m_state[8];
|
||||
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;
|
||||
++s[8];
|
||||
if(s[8] == 0) ++s[9];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
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
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -39,12 +39,7 @@ namespace ModernKeePassLib.Cryptography.Cipher
|
||||
/// </summary>
|
||||
public sealed class StandardAesEngine : ICipherEngine
|
||||
{
|
||||
#if !ModernKeePassLib && !KeePassRT
|
||||
private const CipherMode m_rCipherMode = CipherMode.CBC;
|
||||
private const PaddingMode m_rCipherPadding = PaddingMode.PKCS7;
|
||||
#endif
|
||||
|
||||
private static PwUuid m_uuidAes = null;
|
||||
private static PwUuid g_uuidAes = null;
|
||||
|
||||
/// <summary>
|
||||
/// UUID of the cipher engine. This ID uniquely identifies the
|
||||
@@ -54,26 +49,38 @@ namespace ModernKeePassLib.Cryptography.Cipher
|
||||
{
|
||||
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,
|
||||
0xBE, 0x58, 0x05, 0x21, 0x6A, 0xFC, 0x5A, 0xFF });
|
||||
g_uuidAes = pu;
|
||||
}
|
||||
|
||||
return m_uuidAes;
|
||||
return pu;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the UUID of this cipher engine as <c>PwUuid</c> object.
|
||||
/// </summary>
|
||||
public PwUuid CipherUuid => StandardAesEngine.AesUuid;
|
||||
public PwUuid CipherUuid
|
||||
{
|
||||
get { return StandardAesEngine.AesUuid; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <summary>
|
||||
/// Get a displayable name describing this cipher engine.
|
||||
/// </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)
|
||||
{
|
||||
@@ -90,90 +97,24 @@ namespace ModernKeePassLib.Cryptography.Cipher
|
||||
if(bEncrypt)
|
||||
{
|
||||
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
|
||||
{
|
||||
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)
|
||||
{
|
||||
ValidateArguments(s, bEncrypt, pbKey, pbIV);
|
||||
StandardAesEngine.ValidateArguments(s, bEncrypt, pbKey, pbIV);
|
||||
|
||||
byte[] pbLocalIV = new byte[16];
|
||||
Array.Copy(pbIV, pbLocalIV, 16);
|
||||
|
||||
byte[] pbLocalKey = new byte[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();
|
||||
CbcBlockCipher cbc = new CbcBlockCipher(aes);
|
||||
PaddedBufferedBlockCipher bc = new PaddedBufferedBlockCipher(cbc,
|
||||
@@ -185,19 +126,16 @@ namespace ModernKeePassLib.Cryptography.Cipher
|
||||
IBufferedCipher cpRead = (bEncrypt ? null : bc);
|
||||
IBufferedCipher cpWrite = (bEncrypt ? bc : null);
|
||||
return new CipherStream(s, cpRead, cpWrite);
|
||||
#endif
|
||||
|
||||
//#endif
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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
|
||||
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
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -18,6 +18,7 @@
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
#if ModernKeePassLib
|
||||
using Windows.Security.Cryptography;
|
||||
using ModernKeePassLib.Utility;
|
||||
@@ -27,37 +28,43 @@ using System.Security.Cryptography;
|
||||
#endif
|
||||
using System.IO;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using ModernKeePassLib.Native;
|
||||
|
||||
|
||||
namespace ModernKeePassLib.Cryptography
|
||||
{
|
||||
/// <summary>
|
||||
/// Cryptographically strong random number generator. The returned values
|
||||
/// are unpredictable and cannot be reproduced.
|
||||
/// Cryptographically secure pseudo-random number generator.
|
||||
/// The returned values are unpredictable and cannot be reproduced.
|
||||
/// <c>CryptoRandom</c> is a singleton class.
|
||||
/// </summary>
|
||||
public sealed class CryptoRandom
|
||||
{
|
||||
private byte[] m_pbEntropyPool = new byte[64];
|
||||
private uint m_uCounter;
|
||||
#if ModernKeePassLib
|
||||
//private IRandomNumberGenerator m_rng = NetFxCrypto.RandomNumberGenerator;
|
||||
#else
|
||||
private RNGCryptoServiceProvider m_rng = new RNGCryptoServiceProvider();
|
||||
#endif
|
||||
private ulong m_uCounter;
|
||||
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
|
||||
{
|
||||
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 m_pInstance;
|
||||
return cr;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,10 +91,13 @@ namespace ModernKeePassLib.Cryptography
|
||||
|
||||
private CryptoRandom()
|
||||
{
|
||||
Random r = new Random();
|
||||
m_uCounter = (uint)r.Next();
|
||||
// Random rWeak = new Random(); // Based on tick count
|
||||
// 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());
|
||||
}
|
||||
|
||||
@@ -103,64 +113,64 @@ namespace ModernKeePassLib.Cryptography
|
||||
if(pbEntropy.Length == 0) { Debug.Assert(false); return; }
|
||||
|
||||
byte[] pbNewData = pbEntropy;
|
||||
if(pbEntropy.Length >= 64)
|
||||
if(pbEntropy.Length > 64)
|
||||
{
|
||||
#if ModernKeePassLib
|
||||
/*var shaNew = WinRTCrypto.HashAlgorithmProvider.OpenAlgorithm(HashAlgorithm.Sha512);
|
||||
pbNewData = shaNew.HashData(pbEntropy);*/
|
||||
|
||||
var sha256 = HashAlgorithmProvider.OpenAlgorithm(HashAlgorithmNames.Sha256);
|
||||
var buffer = sha256.HashData(CryptographicBuffer.CreateFromByteArray(pbEntropy));
|
||||
CryptographicBuffer.CopyToByteArray(buffer, out pbNewData);
|
||||
#else
|
||||
|
||||
#if !KeePassLibSD
|
||||
SHA512Managed shaNew = new SHA512Managed();
|
||||
#if KeePassLibSD
|
||||
using(SHA256Managed shaNew = new SHA256Managed())
|
||||
#else
|
||||
SHA256Managed shaNew = new SHA256Managed();
|
||||
using(SHA512Managed shaNew = new SHA512Managed())
|
||||
#endif
|
||||
pbNewData = shaNew.ComputeHash(pbEntropy);
|
||||
|
||||
{
|
||||
pbNewData = shaNew.ComputeHash(pbEntropy);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
MemoryStream ms = new MemoryStream();
|
||||
lock(m_oSyncRoot)
|
||||
{
|
||||
ms.Write(m_pbEntropyPool, 0, m_pbEntropyPool.Length);
|
||||
ms.Write(pbNewData, 0, pbNewData.Length);
|
||||
int cbPool = m_pbEntropyPool.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
|
||||
/*var shaPool = WinRTCrypto.HashAlgorithmProvider.OpenAlgorithm(HashAlgorithm.Sha512);
|
||||
m_pbEntropyPool = shaPool.HashData(pbFinal);*/
|
||||
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);
|
||||
#else
|
||||
|
||||
#if !KeePassLibSD
|
||||
Debug.Assert(pbFinal.Length == (64 + pbNewData.Length));
|
||||
SHA512Managed shaPool = new SHA512Managed();
|
||||
#if KeePassLibSD
|
||||
using(SHA256Managed shaPool = new SHA256Managed())
|
||||
#else
|
||||
SHA256Managed shaPool = new SHA256Managed();
|
||||
using(SHA512Managed shaPool = new SHA512Managed())
|
||||
#endif
|
||||
m_pbEntropyPool = shaPool.ComputeHash(pbFinal);
|
||||
|
||||
{
|
||||
m_pbEntropyPool = shaPool.ComputeHash(pbCmp);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
ms.Dispose();
|
||||
MemUtil.ZeroByteArray(pbCmp);
|
||||
}
|
||||
}
|
||||
|
||||
private static byte[] GetSystemData(Random rWeak)
|
||||
private static byte[] GetSystemData()
|
||||
{
|
||||
MemoryStream ms = new MemoryStream();
|
||||
byte[] pb;
|
||||
|
||||
pb = MemUtil.UInt32ToBytes((uint)Environment.TickCount);
|
||||
ms.Write(pb, 0, pb.Length);
|
||||
pb = MemUtil.Int32ToBytes(Environment.TickCount);
|
||||
MemUtil.Write(ms, pb);
|
||||
|
||||
pb = TimeUtil.PackTime(DateTime.Now);
|
||||
ms.Write(pb, 0, pb.Length);
|
||||
pb = MemUtil.Int64ToBytes(DateTime.UtcNow.ToBinary());
|
||||
MemUtil.Write(ms, pb);
|
||||
|
||||
#if (!ModernKeePassLib && !KeePassLibSD && !KeePassRT)
|
||||
// In try-catch for systems without GUI;
|
||||
@@ -168,95 +178,138 @@ namespace ModernKeePassLib.Cryptography
|
||||
try
|
||||
{
|
||||
Point pt = Cursor.Position;
|
||||
pb = MemUtil.UInt32ToBytes((uint)pt.X);
|
||||
ms.Write(pb, 0, pb.Length);
|
||||
pb = MemUtil.UInt32ToBytes((uint)pt.Y);
|
||||
ms.Write(pb, 0, pb.Length);
|
||||
pb = MemUtil.Int32ToBytes(pt.X);
|
||||
MemUtil.Write(ms, pb);
|
||||
pb = MemUtil.Int32ToBytes(pt.Y);
|
||||
MemUtil.Write(ms, pb);
|
||||
}
|
||||
catch(Exception) { }
|
||||
#endif
|
||||
|
||||
pb = MemUtil.UInt32ToBytes((uint)rWeak.Next());
|
||||
ms.Write(pb, 0, pb.Length);
|
||||
|
||||
#if ModernKeePassLib
|
||||
pb = MemUtil.UInt32ToBytes((uint)Environment.ProcessorCount);
|
||||
ms.Write(pb, 0, pb.Length);
|
||||
pb = MemUtil.UInt32ToBytes((uint)Environment.ProcessorCount);
|
||||
ms.Write(pb, 0, pb.Length);
|
||||
|
||||
pb = MemUtil.UInt32ToBytes((uint)Environment.CurrentManagedThreadId);
|
||||
ms.Write(pb, 0, pb.Length);
|
||||
pb = MemUtil.UInt32ToBytes((uint)Environment.CurrentManagedThreadId);
|
||||
ms.Write(pb, 0, pb.Length);
|
||||
#else
|
||||
pb = MemUtil.UInt32ToBytes((uint)NativeLib.GetPlatformID());
|
||||
ms.Write(pb, 0, pb.Length);
|
||||
#endif
|
||||
pb = MemUtil.UInt32ToBytes((uint)NativeLib.GetPlatformID());
|
||||
MemUtil.Write(ms, pb);
|
||||
|
||||
#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;
|
||||
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();
|
||||
|
||||
pb = MemUtil.UInt64ToBytes((ulong)p.Handle.ToInt64());
|
||||
ms.Write(pb, 0, pb.Length);
|
||||
pb = MemUtil.UInt32ToBytes((uint)p.HandleCount);
|
||||
ms.Write(pb, 0, pb.Length);
|
||||
pb = MemUtil.UInt32ToBytes((uint)p.Id);
|
||||
ms.Write(pb, 0, pb.Length);
|
||||
pb = MemUtil.UInt64ToBytes((ulong)p.NonpagedSystemMemorySize64);
|
||||
ms.Write(pb, 0, pb.Length);
|
||||
pb = MemUtil.UInt64ToBytes((ulong)p.PagedMemorySize64);
|
||||
ms.Write(pb, 0, pb.Length);
|
||||
pb = MemUtil.UInt64ToBytes((ulong)p.PagedSystemMemorySize64);
|
||||
ms.Write(pb, 0, pb.Length);
|
||||
pb = MemUtil.UInt64ToBytes((ulong)p.PeakPagedMemorySize64);
|
||||
ms.Write(pb, 0, pb.Length);
|
||||
pb = MemUtil.UInt64ToBytes((ulong)p.PeakVirtualMemorySize64);
|
||||
ms.Write(pb, 0, pb.Length);
|
||||
pb = MemUtil.UInt64ToBytes((ulong)p.PeakWorkingSet64);
|
||||
ms.Write(pb, 0, pb.Length);
|
||||
pb = MemUtil.UInt64ToBytes((ulong)p.PrivateMemorySize64);
|
||||
ms.Write(pb, 0, pb.Length);
|
||||
pb = MemUtil.UInt64ToBytes((ulong)p.StartTime.ToBinary());
|
||||
ms.Write(pb, 0, pb.Length);
|
||||
pb = MemUtil.UInt64ToBytes((ulong)p.VirtualMemorySize64);
|
||||
ms.Write(pb, 0, pb.Length);
|
||||
pb = MemUtil.UInt64ToBytes((ulong)p.WorkingSet64);
|
||||
ms.Write(pb, 0, pb.Length);
|
||||
pb = MemUtil.Int64ToBytes(p.Handle.ToInt64());
|
||||
MemUtil.Write(ms, pb);
|
||||
pb = MemUtil.Int32ToBytes(p.HandleCount);
|
||||
MemUtil.Write(ms, pb);
|
||||
pb = MemUtil.Int32ToBytes(p.Id);
|
||||
MemUtil.Write(ms, pb);
|
||||
pb = MemUtil.Int64ToBytes(p.NonpagedSystemMemorySize64);
|
||||
MemUtil.Write(ms, pb);
|
||||
pb = MemUtil.Int64ToBytes(p.PagedMemorySize64);
|
||||
MemUtil.Write(ms, pb);
|
||||
pb = MemUtil.Int64ToBytes(p.PagedSystemMemorySize64);
|
||||
MemUtil.Write(ms, pb);
|
||||
pb = MemUtil.Int64ToBytes(p.PeakPagedMemorySize64);
|
||||
MemUtil.Write(ms, pb);
|
||||
pb = MemUtil.Int64ToBytes(p.PeakVirtualMemorySize64);
|
||||
MemUtil.Write(ms, pb);
|
||||
pb = MemUtil.Int64ToBytes(p.PeakWorkingSet64);
|
||||
MemUtil.Write(ms, pb);
|
||||
pb = MemUtil.Int64ToBytes(p.PrivateMemorySize64);
|
||||
MemUtil.Write(ms, pb);
|
||||
pb = MemUtil.Int64ToBytes(p.StartTime.ToBinary());
|
||||
MemUtil.Write(ms, pb);
|
||||
pb = MemUtil.Int64ToBytes(p.VirtualMemorySize64);
|
||||
MemUtil.Write(ms, pb);
|
||||
pb = MemUtil.Int64ToBytes(p.WorkingSet64);
|
||||
MemUtil.Write(ms, pb);
|
||||
|
||||
// Not supported in Mono 1.2.6:
|
||||
// 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
|
||||
{
|
||||
try { if(p != null) p.Dispose(); }
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
}
|
||||
#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();
|
||||
ms.Write(pb, 0, pb.Length);
|
||||
MemUtil.Write(ms, pb);
|
||||
|
||||
byte[] pbAll = ms.ToArray();
|
||||
ms.Dispose();
|
||||
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()
|
||||
{
|
||||
byte[] pbCspRandom = new byte[32];
|
||||
//m_rng.GetBytes(pbCspRandom);
|
||||
CryptographicBuffer.CopyToByteArray(CryptographicBuffer.GenerateRandom(32), out pbCspRandom);
|
||||
CryptographicBuffer.CopyToByteArray(CryptographicBuffer.GenerateRandom(32), out pbCspRandom);
|
||||
return pbCspRandom;
|
||||
}
|
||||
|
||||
@@ -265,39 +318,32 @@ namespace ModernKeePassLib.Cryptography
|
||||
if(this.GenerateRandom256Pre != null)
|
||||
this.GenerateRandom256Pre(this, EventArgs.Empty);
|
||||
|
||||
byte[] pbFinal;
|
||||
byte[] pbCmp;
|
||||
lock(m_oSyncRoot)
|
||||
{
|
||||
unchecked { m_uCounter += 386047; } // Prime number
|
||||
byte[] pbCounter = MemUtil.UInt32ToBytes(m_uCounter);
|
||||
m_uCounter += 0x74D8B29E4D38E161UL; // Prime number
|
||||
byte[] pbCounter = MemUtil.UInt64ToBytes(m_uCounter);
|
||||
|
||||
byte[] pbCspRandom = GetCspData();
|
||||
|
||||
MemoryStream ms = new MemoryStream();
|
||||
ms.Write(m_pbEntropyPool, 0, m_pbEntropyPool.Length);
|
||||
ms.Write(pbCounter, 0, pbCounter.Length);
|
||||
ms.Write(pbCspRandom, 0, pbCspRandom.Length);
|
||||
pbFinal = ms.ToArray();
|
||||
Debug.Assert(pbFinal.Length == (m_pbEntropyPool.Length +
|
||||
pbCounter.Length + pbCspRandom.Length));
|
||||
ms.Dispose();
|
||||
int cbPool = m_pbEntropyPool.Length;
|
||||
int cbCtr = pbCounter.Length;
|
||||
int cbCsp = pbCspRandom.Length;
|
||||
|
||||
pbCmp = new byte[cbPool + cbCtr + cbCsp];
|
||||
Array.Copy(m_pbEntropyPool, pbCmp, cbPool);
|
||||
Array.Copy(pbCounter, 0, pbCmp, cbPool, cbCtr);
|
||||
Array.Copy(pbCspRandom, 0, pbCmp, cbPool + cbCtr, cbCsp);
|
||||
|
||||
MemUtil.ZeroByteArray(pbCspRandom);
|
||||
|
||||
m_uGeneratedBytesCount += 32;
|
||||
}
|
||||
|
||||
#if ModernKeePassLib
|
||||
/*var sha256 = WinRTCrypto.HashAlgorithmProvider.OpenAlgorithm(HashAlgorithm.Sha256);
|
||||
return sha256.HashData(pbFinal);*/
|
||||
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
|
||||
}
|
||||
byte[] pbRet = CryptoUtil.HashSha256(pbCmp);
|
||||
MemUtil.ZeroByteArray(pbCmp);
|
||||
return pbRet;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a number of cryptographically strong random bytes.
|
||||
@@ -308,30 +354,54 @@ namespace ModernKeePassLib.Cryptography
|
||||
/// random bytes.</returns>
|
||||
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];
|
||||
long lPos = 0;
|
||||
int cbRem = (int)uRequestedBytes;
|
||||
byte[] pbRes = new byte[cbRem];
|
||||
int iPos = 0;
|
||||
|
||||
while(uRequestedBytes != 0)
|
||||
while(cbRem != 0)
|
||||
{
|
||||
byte[] pbRandom256 = GenerateRandom256();
|
||||
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)
|
||||
Array.Copy(pbRandom256, 0, pbRes, lPos, lCopy);
|
||||
#else
|
||||
Array.Copy(pbRandom256, 0, pbRes, (int)lPos, (int)lCopy);
|
||||
#endif
|
||||
MemUtil.ZeroByteArray(pbRandom256);
|
||||
|
||||
lPos += lCopy;
|
||||
uRequestedBytes -= (uint)lCopy;
|
||||
iPos += cbCopy;
|
||||
cbRem -= cbCopy;
|
||||
}
|
||||
|
||||
Debug.Assert((int)lPos == pbRes.Length);
|
||||
Debug.Assert(iPos == pbRes.Length);
|
||||
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
|
||||
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
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -19,14 +19,11 @@
|
||||
|
||||
using System;
|
||||
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.Utility;
|
||||
|
||||
namespace ModernKeePassLib.Cryptography
|
||||
{
|
||||
@@ -42,6 +39,7 @@ namespace ModernKeePassLib.Cryptography
|
||||
|
||||
/// <summary>
|
||||
/// A variant of the ARCFour algorithm (RC4 incompatible).
|
||||
/// Insecure; for backward compatibility only.
|
||||
/// </summary>
|
||||
ArcFourVariant = 1,
|
||||
|
||||
@@ -50,7 +48,12 @@ namespace ModernKeePassLib.Cryptography
|
||||
/// </summary>
|
||||
Salsa20 = 2,
|
||||
|
||||
Count = 3
|
||||
/// <summary>
|
||||
/// ChaCha20 stream cipher algorithm.
|
||||
/// </summary>
|
||||
ChaCha20 = 3,
|
||||
|
||||
Count = 4
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -59,47 +62,81 @@ namespace ModernKeePassLib.Cryptography
|
||||
/// properties, but for the same seed always the same stream
|
||||
/// is produced, i.e. this class can be used as stream cipher.
|
||||
/// </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_i = 0;
|
||||
private byte m_j = 0;
|
||||
|
||||
private Salsa20Cipher m_salsa20 = null;
|
||||
private ChaCha20Cipher m_chacha20 = null;
|
||||
|
||||
/// <summary>
|
||||
/// Construct a new cryptographically secure random stream object.
|
||||
/// </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
|
||||
/// must contain at least 1 byte.</param>
|
||||
/// <exception cref="System.ArgumentNullException">Thrown if the
|
||||
/// <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)
|
||||
public CryptoRandomStream(CrsAlgorithm a, 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;
|
||||
Debug.Assert(uKeyLen != 0); if(uKeyLen == 0) throw new ArgumentException();
|
||||
m_crsAlgorithm = a;
|
||||
|
||||
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
|
||||
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
|
||||
{
|
||||
byte j = 0, t;
|
||||
uint inxKey = 0;
|
||||
for(uint w = 0; w < 256; ++w) // Key setup
|
||||
int inxKey = 0;
|
||||
for(int w = 0; w < 256; ++w) // Key setup
|
||||
{
|
||||
j += (byte)(m_pbState[w] + pbKey[inxKey]);
|
||||
|
||||
@@ -108,34 +145,42 @@ namespace ModernKeePassLib.Cryptography
|
||||
m_pbState[j] = t;
|
||||
|
||||
++inxKey;
|
||||
if(inxKey >= uKeyLen) inxKey = 0;
|
||||
if(inxKey >= cbKey) inxKey = 0;
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
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>
|
||||
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
|
||||
{
|
||||
for(uint w = 0; w < uRequestedCount; ++w)
|
||||
for(int w = 0; w < cb; ++w)
|
||||
{
|
||||
++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); }
|
||||
|
||||
return pbRet;
|
||||
@@ -178,14 +230,7 @@ namespace ModernKeePassLib.Cryptography
|
||||
public ulong GetRandomUInt64()
|
||||
{
|
||||
byte[] pb = GetRandomBytes(8);
|
||||
|
||||
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);
|
||||
}
|
||||
return MemUtil.BytesToUInt64(pb);
|
||||
}
|
||||
|
||||
#if CRSBENCHMARK
|
||||
@@ -211,8 +256,10 @@ namespace ModernKeePassLib.Cryptography
|
||||
int nStart = Environment.TickCount;
|
||||
for(int i = 0; i < nRounds; ++i)
|
||||
{
|
||||
CryptoRandomStream c = new CryptoRandomStream(cra, pbKey);
|
||||
c.GetRandomBytes((uint)nDataSize);
|
||||
using(CryptoRandomStream c = new CryptoRandomStream(cra, pbKey))
|
||||
{
|
||||
c.GetRandomBytes((uint)nDataSize);
|
||||
}
|
||||
}
|
||||
int nEnd = Environment.TickCount;
|
||||
|
||||
|
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
|
||||
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
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
#if ModernKeePassLib
|
||||
using Windows.Security.Cryptography;
|
||||
@@ -29,7 +29,6 @@ using Org.BouncyCastle.Crypto.Digests;
|
||||
#else
|
||||
using System.Security.Cryptography;
|
||||
#endif
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices.ComTypes;
|
||||
using ModernKeePassLib.Utility;
|
||||
using Org.BouncyCastle.Crypto.Tls;
|
||||
@@ -38,12 +37,12 @@ namespace ModernKeePassLib.Cryptography
|
||||
{
|
||||
public sealed class HashingStreamEx : Stream
|
||||
{
|
||||
private Stream m_sBaseStream;
|
||||
private bool m_bWriting;
|
||||
#if ModernKeePassLib
|
||||
private readonly Stream m_sBaseStream;
|
||||
private readonly bool m_bWriting;
|
||||
#if ModernKeePassLib
|
||||
//private ICryptoTransform m_hash;
|
||||
//private CryptographicHash m_hash;
|
||||
private IDigest m_hash;
|
||||
private IDigest m_hash;
|
||||
#else
|
||||
private HashAlgorithm m_hash;
|
||||
#endif
|
||||
@@ -78,7 +77,7 @@ namespace ModernKeePassLib.Cryptography
|
||||
public override long Position
|
||||
{
|
||||
get { return m_sBaseStream.Position; }
|
||||
set { throw new NotSupportedException(); }
|
||||
set { Debug.Assert(false); throw new NotSupportedException(); }
|
||||
}
|
||||
|
||||
#if ModernKeePassLib
|
||||
@@ -88,8 +87,7 @@ namespace ModernKeePassLib.Cryptography
|
||||
public HashingStreamEx(Stream sBaseStream, bool bWriting, HashAlgorithm hashAlgorithm)
|
||||
#endif
|
||||
{
|
||||
if(sBaseStream == null)
|
||||
throw new ArgumentNullException("sBaseStream");
|
||||
if (sBaseStream == null) throw new ArgumentNullException("sBaseStream");
|
||||
|
||||
m_sBaseStream = sBaseStream;
|
||||
m_bWriting = bWriting;
|
||||
@@ -107,53 +105,51 @@ namespace ModernKeePassLib.Cryptography
|
||||
try { if(m_hash == null) m_hash = HashAlgorithm.Create(); }
|
||||
catch(Exception) { }
|
||||
#endif
|
||||
if (m_hash == null) { Debug.Assert(false); return; }
|
||||
if(m_hash == null) { Debug.Assert(false); return; }
|
||||
|
||||
// Validate hash algorithm
|
||||
/*if((!m_hash.CanReuseTransform) || (!m_hash.CanTransformMultipleBlocks) ||
|
||||
(m_hash.InputBlockSize != 1) || (m_hash.OutputBlockSize != 1))
|
||||
/*if(!m_hash.CanReuseTransform || !m_hash.CanTransformMultipleBlocks)
|
||||
{
|
||||
#if false && DEBUG
|
||||
MessageService.ShowWarning("Broken HashAlgorithm object in HashingStreamEx.");
|
||||
#endif
|
||||
Debug.Assert(false);
|
||||
m_hash = null;
|
||||
}*/
|
||||
}
|
||||
|
||||
public override void Flush()
|
||||
{
|
||||
m_sBaseStream.Flush();
|
||||
}
|
||||
|
||||
#if ModernKeePassLib || KeePassRT
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if(!disposing) return;
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (!disposing) return;
|
||||
#else
|
||||
public override void Close()
|
||||
{
|
||||
#endif
|
||||
if(m_hash != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
//m_hash.TransformFinalBlock(new byte[0], 0, 0);
|
||||
if (m_hash != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
//m_hash.TransformFinalBlock(new byte[0], 0, 0);
|
||||
#if ModernKeePassLib
|
||||
//m_pbFinalHash = (m_hash as CryptographicHash).GetValueAndReset ();
|
||||
//CryptographicBuffer.CopyToByteArray(m_hash.GetValueAndReset(), out m_pbFinalHash);
|
||||
m_pbFinalHash = new byte[32];
|
||||
m_hash.DoFinal(m_pbFinalHash, 0);
|
||||
m_hash.Reset();
|
||||
//m_pbFinalHash = (m_hash as CryptographicHash).GetValueAndReset ();
|
||||
//CryptographicBuffer.CopyToByteArray(m_hash.GetValueAndReset(), out m_pbFinalHash);
|
||||
m_pbFinalHash = new byte[32];
|
||||
m_hash.DoFinal(m_pbFinalHash, 0);
|
||||
m_hash.Reset();
|
||||
#else
|
||||
m_pbFinalHash = m_hash.Hash;
|
||||
#endif
|
||||
}
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
Debug.Assert(false);
|
||||
}
|
||||
|
||||
m_hash = null;
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
}
|
||||
|
||||
m_sBaseStream.Dispose();
|
||||
public override void Flush()
|
||||
{
|
||||
m_sBaseStream.Flush();
|
||||
}
|
||||
|
||||
public override long Seek(long lOffset, SeekOrigin soOrigin)
|
||||
@@ -185,12 +181,11 @@ namespace ModernKeePassLib.Cryptography
|
||||
#endif
|
||||
|
||||
if((m_hash != null) && (nRead > 0))
|
||||
//m_hash.TransformBlock(pbBuffer, nOffset, nRead, pbBuffer, nOffset);
|
||||
//m_hash.Append(CryptographicBuffer.CreateFromByteArray(pbBuffer));
|
||||
m_hash.BlockUpdate(pbBuffer, nOffset, nRead);
|
||||
//m_hash.TransformBlock(pbBuffer, nOffset, nRead, pbBuffer, nOffset);
|
||||
m_hash.BlockUpdate(pbBuffer, nOffset, nRead);
|
||||
|
||||
#if DEBUG
|
||||
Debug.Assert(MemUtil.ArraysEqual(pbBuffer, pbOrg));
|
||||
Debug.Assert(MemUtil.ArraysEqual(pbBuffer, pbOrg));
|
||||
#endif
|
||||
|
||||
return nRead;
|
||||
@@ -205,12 +200,12 @@ namespace ModernKeePassLib.Cryptography
|
||||
Array.Copy(pbBuffer, pbOrg, pbBuffer.Length);
|
||||
#endif
|
||||
|
||||
if ((m_hash != null) && (nCount > 0))
|
||||
//m_hash.TransformBlock(pbBuffer, nOffset, nCount, pbBuffer, nOffset);
|
||||
//m_hash.Append(CryptographicBuffer.CreateFromByteArray(pbBuffer));
|
||||
m_hash.BlockUpdate(pbBuffer, nOffset, nCount);
|
||||
if((m_hash != null) && (nCount > 0))
|
||||
//m_hash.TransformBlock(pbBuffer, nOffset, nCount, pbBuffer, nOffset);
|
||||
m_hash.BlockUpdate(pbBuffer, nOffset, nCount);
|
||||
|
||||
#if DEBUG
|
||||
Debug.Assert(MemUtil.ArraysEqual(pbBuffer, pbOrg));
|
||||
Debug.Assert(MemUtil.ArraysEqual(pbBuffer, pbOrg));
|
||||
#endif
|
||||
|
||||
m_sBaseStream.Write(pbBuffer, nOffset, nCount);
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
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
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -45,22 +45,14 @@ namespace ModernKeePassLib.Cryptography
|
||||
uint uCodeDigits, bool bAddChecksum, int iTruncationOffset)
|
||||
{
|
||||
byte[] pbText = MemUtil.UInt64ToBytes(uFactor);
|
||||
Array.Reverse(pbText); // Big-Endian
|
||||
|
||||
#if ModernKeePassLib
|
||||
/*var hsha1 = WinRTCrypto.MacAlgorithmProvider.OpenAlgorithm(MacAlgorithm.HmacSha1).CreateHash(pbSecret);
|
||||
hsha1.Append(pbText);
|
||||
var pbHash = hsha1.GetValueAndReset();*/
|
||||
Array.Reverse(pbText); // To big-endian
|
||||
|
||||
var hsha1 = MacAlgorithmProvider.OpenAlgorithm(MacAlgorithmNames.HmacSha1).CreateHash(CryptographicBuffer.CreateFromByteArray(pbSecret));
|
||||
hsha1.Append(CryptographicBuffer.CreateFromByteArray(pbText));
|
||||
byte[] 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)))
|
||||
uOffset = (uint)iTruncationOffset;
|
||||
|
||||
|
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
|
||||
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
|
||||
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)
|
||||
{
|
||||
Array.Clear(vGenerated, 0, vGenerated.Length);
|
||||
MemUtil.ZeroArray<char>(vGenerated);
|
||||
return PwgError.TooFewCharacters;
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ namespace ModernKeePassLib.Cryptography.PasswordGenerator
|
||||
byte[] pbUtf8 = StrUtil.Utf8.GetBytes(vGenerated);
|
||||
psOut = new ProtectedString(true, pbUtf8);
|
||||
MemUtil.ZeroByteArray(pbUtf8);
|
||||
Array.Clear(vGenerated, 0, vGenerated.Length);
|
||||
MemUtil.ZeroArray<char>(vGenerated);
|
||||
|
||||
return PwgError.Success;
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
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);
|
||||
psOut = new ProtectedString(true, pbUtf8);
|
||||
MemUtil.ZeroByteArray(pbUtf8);
|
||||
Array.Clear(vArray, 0, vArray.Length);
|
||||
MemUtil.ZeroArray<char>(vArray);
|
||||
vGenerated.Clear();
|
||||
|
||||
return PwgError.Success;
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
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
|
||||
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
|
||||
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
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -19,10 +19,13 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
using System.Diagnostics;
|
||||
|
||||
using Windows.Security.Cryptography;
|
||||
using Windows.Security.Cryptography.Core;
|
||||
using ModernKeePassLib.Security;
|
||||
using ModernKeePassLib.Utility;
|
||||
|
||||
namespace ModernKeePassLib.Cryptography.PasswordGenerator
|
||||
{
|
||||
@@ -46,32 +49,56 @@ namespace ModernKeePassLib.Cryptography.PasswordGenerator
|
||||
Debug.Assert(pwProfile != null);
|
||||
if(pwProfile == null) throw new ArgumentNullException("pwProfile");
|
||||
|
||||
CryptoRandomStream crs = CreateCryptoStream(pbUserEntropy);
|
||||
PwgError e = PwgError.Unknown;
|
||||
CryptoRandomStream crs = null;
|
||||
byte[] pbKey = null;
|
||||
try
|
||||
{
|
||||
crs = CreateRandomStream(pbUserEntropy, out pbKey);
|
||||
|
||||
if(pwProfile.GeneratorType == PasswordGeneratorType.CharSet)
|
||||
e = CharSetBasedGenerator.Generate(out psOut, pwProfile, crs);
|
||||
else if(pwProfile.GeneratorType == PasswordGeneratorType.Pattern)
|
||||
e = PatternBasedGenerator.Generate(out psOut, pwProfile, crs);
|
||||
else if(pwProfile.GeneratorType == PasswordGeneratorType.Custom)
|
||||
e = GenerateCustom(out psOut, pwProfile, crs, pwAlgorithmPool);
|
||||
else { Debug.Assert(false); psOut = ProtectedString.Empty; }
|
||||
if(pwProfile.GeneratorType == PasswordGeneratorType.CharSet)
|
||||
e = CharSetBasedGenerator.Generate(out psOut, pwProfile, crs);
|
||||
else if(pwProfile.GeneratorType == PasswordGeneratorType.Pattern)
|
||||
e = PatternBasedGenerator.Generate(out psOut, pwProfile, crs);
|
||||
else if(pwProfile.GeneratorType == PasswordGeneratorType.Custom)
|
||||
e = GenerateCustom(out psOut, pwProfile, crs, pwAlgorithmPool);
|
||||
else { Debug.Assert(false); psOut = ProtectedString.Empty; }
|
||||
}
|
||||
finally
|
||||
{
|
||||
if(crs != null) crs.Dispose();
|
||||
if(pbKey != null) MemUtil.ZeroByteArray(pbKey);
|
||||
}
|
||||
|
||||
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
|
||||
Debug.Assert(pbKey.Length >= 64);
|
||||
if((pbAdditionalEntropy != null) && (pbAdditionalEntropy.Length > 0))
|
||||
{
|
||||
for(int nKeyPos = 0; nKeyPos < pbKey.Length; ++nKeyPos)
|
||||
pbKey[nKeyPos] ^= pbAdditionalEntropy[nKeyPos % pbAdditionalEntropy.Length];
|
||||
#if ModernKeePassLib
|
||||
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,
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
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 void EnsureInitialized()
|
||||
@@ -422,7 +422,7 @@ namespace ModernKeePassLib.Cryptography
|
||||
|
||||
char[] vChars = StrUtil.Utf8.GetChars(pbUnprotectedUtf8);
|
||||
uint uResult = EstimatePasswordBits(vChars);
|
||||
Array.Clear(vChars, 0, vChars.Length);
|
||||
MemUtil.ZeroArray<char>(vChars);
|
||||
|
||||
return uResult;
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
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
|
||||
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.Globalization;
|
||||
using System.Diagnostics;
|
||||
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices.WindowsRuntime;
|
||||
using Windows.Security.Cryptography.Core;
|
||||
using ModernKeePassLib.Cryptography.Cipher;
|
||||
using ModernKeePassLib.Cryptography.Hash;
|
||||
using ModernKeePassLib.Cryptography.KeyDerivation;
|
||||
using ModernKeePassLib.Keys;
|
||||
using ModernKeePassLib.Native;
|
||||
using ModernKeePassLib.Utility;
|
||||
using ModernKeePassLib.Resources;
|
||||
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
|
||||
{
|
||||
/* /// <summary>
|
||||
/// Return values of the <c>SelfTest.Perform</c> method.
|
||||
/// </summary>
|
||||
public enum SelfTestResult
|
||||
{
|
||||
Success = 0,
|
||||
RijndaelEcbError = 1,
|
||||
Salsa20Error = 2,
|
||||
NativeKeyTransformationError = 3
|
||||
} */
|
||||
|
||||
/// <summary>
|
||||
/// Class containing self-test methods.
|
||||
/// </summary>
|
||||
// TODO: move all this into the Unit Tests project
|
||||
public static class SelfTest
|
||||
{
|
||||
/// <summary>
|
||||
@@ -59,47 +58,63 @@ namespace ModernKeePassLib.Cryptography
|
||||
/// </summary>
|
||||
public static void Perform()
|
||||
{
|
||||
Random r = CryptoRandom.NewWeakRandom();
|
||||
|
||||
TestFipsComplianceProblems(); // Must be the first test
|
||||
|
||||
TestRijndael();
|
||||
TestSalsa20();
|
||||
TestAes();
|
||||
TestSalsa20(r);
|
||||
TestChaCha20(r);
|
||||
TestBlake2b(r);
|
||||
TestArgon2();
|
||||
TestHmac();
|
||||
|
||||
TestKeyTransform(r);
|
||||
#if !ModernKeePassLib
|
||||
TestNativeKeyTransform();
|
||||
TestNativeKeyTransform(r);
|
||||
#endif
|
||||
|
||||
TestHmacOtp();
|
||||
|
||||
TestProtectedObjects();
|
||||
TestMemUtil();
|
||||
TestProtectedObjects(r);
|
||||
TestMemUtil(r);
|
||||
TestStrUtil();
|
||||
TestUrlUtil();
|
||||
|
||||
Debug.Assert((int)PwIcon.World == 1);
|
||||
Debug.Assert((int)PwIcon.Warning == 2);
|
||||
Debug.Assert((int)PwIcon.BlackBerry == 68);
|
||||
|
||||
#if KeePassUAP
|
||||
SelfTestEx.Perform();
|
||||
#endif
|
||||
}
|
||||
|
||||
internal static void TestFipsComplianceProblems()
|
||||
{
|
||||
#if !ModernKeePassLib && !KeePassRT
|
||||
try { new RijndaelManaged(); }
|
||||
#if !ModernKeePassLib
|
||||
try { using(RijndaelManaged r = new RijndaelManaged()) { } }
|
||||
catch(Exception exAes)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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)
|
||||
byte[] pbIV = new byte[16];
|
||||
byte[] pbTestKey = new byte[32];
|
||||
@@ -114,31 +129,38 @@ namespace ModernKeePassLib.Cryptography
|
||||
for(i = 0; i < 16; ++i) pbTestData[i] = 0;
|
||||
pbTestData[0] = 0x04;
|
||||
|
||||
RijndaelManaged r = new RijndaelManaged();
|
||||
|
||||
if(r.BlockSize != 128) // AES block size
|
||||
#if ModernKeePassLib
|
||||
AesEngine r = new AesEngine();
|
||||
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);
|
||||
r.BlockSize = 128;
|
||||
a.BlockSize = 128;
|
||||
}
|
||||
|
||||
r.IV = pbIV;
|
||||
r.KeySize = 256;
|
||||
r.Key = pbTestKey;
|
||||
r.Mode = CipherMode.ECB;
|
||||
ICryptoTransform iCrypt = r.CreateEncryptor();
|
||||
a.IV = pbIV;
|
||||
a.KeySize = 256;
|
||||
a.Key = pbTestKey;
|
||||
a.Mode = CipherMode.ECB;
|
||||
ICryptoTransform iCrypt = a.CreateEncryptor();
|
||||
|
||||
iCrypt.TransformBlock(pbTestData, 0, 16, pbTestData, 0);
|
||||
#endif
|
||||
|
||||
if(!MemUtil.ArraysEqual(pbTestData, pbReferenceCT))
|
||||
throw new SecurityException(KLRes.EncAlgorithmAes + ".");
|
||||
#endif
|
||||
throw new SecurityException("AES");
|
||||
}
|
||||
|
||||
private static void TestSalsa20()
|
||||
private static void TestSalsa20(Random r)
|
||||
{
|
||||
#if DEBUG
|
||||
// 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,
|
||||
0xA7, 0xFA, 0x4D, 0xA0, 0xF3, 0x46, 0x99, 0xEC,
|
||||
0x3F, 0x92, 0xE5, 0x38, 0x8B, 0xDE, 0x31, 0x84,
|
||||
@@ -153,12 +175,11 @@ namespace ModernKeePassLib.Cryptography
|
||||
|
||||
byte[] pb = new byte[16];
|
||||
Salsa20Cipher c = new Salsa20Cipher(pbKey, pbIV);
|
||||
c.Encrypt(pb, pb.Length, false);
|
||||
c.Encrypt(pb, 0, pb.Length);
|
||||
if(!MemUtil.ArraysEqual(pb, pbExpected))
|
||||
throw new SecurityException("Salsa20-1");
|
||||
|
||||
#if DEBUG
|
||||
// Extended test in debug mode
|
||||
// Extended test
|
||||
byte[] pbExpected2 = new byte[16] {
|
||||
0xAB, 0xF3, 0x9A, 0x21, 0x0E, 0xEE, 0x89, 0x59,
|
||||
0x8B, 0x71, 0x33, 0x37, 0x70, 0x56, 0xC2, 0xFE
|
||||
@@ -168,15 +189,15 @@ namespace ModernKeePassLib.Cryptography
|
||||
0x28, 0xF5, 0x67, 0x91, 0xD5, 0xB7, 0xCE, 0x23
|
||||
};
|
||||
|
||||
Random r = new Random();
|
||||
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))
|
||||
throw new SecurityException("Salsa20-2");
|
||||
|
||||
nPos = Salsa20ToPos(c, r, nPos + pb.Length, 131008);
|
||||
Array.Clear(pb, 0, pb.Length);
|
||||
c.Encrypt(pb, pb.Length, true);
|
||||
c.Encrypt(pb, 0, pb.Length);
|
||||
if(!MemUtil.ArraysEqual(pb, pbExpected3))
|
||||
throw new SecurityException("Salsa20-3");
|
||||
|
||||
@@ -185,8 +206,8 @@ namespace ModernKeePassLib.Cryptography
|
||||
for(int i = 0; i < nRounds; ++i)
|
||||
{
|
||||
byte[] z = new byte[32];
|
||||
c = new Salsa20Cipher(z, BitConverter.GetBytes((long)i));
|
||||
c.Encrypt(z, z.Length, true);
|
||||
c = new Salsa20Cipher(z, MemUtil.Int64ToBytes(i));
|
||||
c.Encrypt(z, 0, z.Length);
|
||||
d[MemUtil.ByteArrayToHexString(z)] = true;
|
||||
}
|
||||
if(d.Count != nRounds) throw new SecurityException("Salsa20-4");
|
||||
@@ -203,7 +224,7 @@ namespace ModernKeePassLib.Cryptography
|
||||
{
|
||||
int x = r.Next(1, 513);
|
||||
int nGen = Math.Min(nTargetPos - nPos, x);
|
||||
c.Encrypt(pb, nGen, r.Next(0, 2) == 0);
|
||||
c.Encrypt(pb, 0, nGen);
|
||||
nPos += nGen;
|
||||
}
|
||||
|
||||
@@ -211,34 +232,571 @@ namespace ModernKeePassLib.Cryptography
|
||||
}
|
||||
#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
|
||||
private static void TestNativeKeyTransform()
|
||||
{
|
||||
#if DEBUG
|
||||
byte[] pbOrgKey = 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];
|
||||
Array.Copy(pbOrgKey, pbManaged, 32);
|
||||
if(CompositeKey.TransformKeyManaged(pbManaged, pbSeed, uRounds) == false)
|
||||
throw new SecurityException("Managed transform.");
|
||||
if(!AesKdf.TransformKeyManaged(pbManaged, pbSeed, uRounds))
|
||||
throw new SecurityException("AES-KDF-1");
|
||||
|
||||
byte[] pbNative = new byte[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")
|
||||
|
||||
if(!MemUtil.ArraysEqual(pbManaged, pbNative))
|
||||
throw new SecurityException("Native transform.");
|
||||
throw new SecurityException("AES-KDF-2");
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
private static void TestMemUtil()
|
||||
private static void TestMemUtil(Random r)
|
||||
{
|
||||
#if DEBUG
|
||||
Random r = new Random();
|
||||
byte[] pb = CryptoRandom.Instance.GetRandomBytes((uint)r.Next(
|
||||
0, 0x2FFFF));
|
||||
|
||||
@@ -297,12 +855,21 @@ namespace ModernKeePassLib.Cryptography
|
||||
pbRes = MemUtil.ParseBase32("JNSXSIDQOJXXM2LEMVZCAYTBONSWIIDPNYQG63TFFV2GS3LFEBYGC43TO5XXEZDTFY======");
|
||||
pbExp = Encoding.UTF8.GetBytes("Key provider based on one-time passwords.");
|
||||
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
|
||||
}
|
||||
|
||||
private static void TestHmacOtp()
|
||||
{
|
||||
#if (DEBUG && !KeePassLibSD && !KeePassRT)
|
||||
#if (DEBUG && !KeePassLibSD)
|
||||
byte[] pbSecret = StrUtil.Utf8.GetBytes("12345678901234567890");
|
||||
string[] vExp = new string[]{ "755224", "287082", "359152",
|
||||
"969429", "338314", "254676", "287922", "162583", "399871",
|
||||
@@ -316,7 +883,7 @@ namespace ModernKeePassLib.Cryptography
|
||||
#endif
|
||||
}
|
||||
|
||||
private static void TestProtectedObjects()
|
||||
private static void TestProtectedObjects(Random r)
|
||||
{
|
||||
#if DEBUG
|
||||
Encoding enc = StrUtil.Utf8;
|
||||
@@ -371,7 +938,6 @@ namespace ModernKeePassLib.Cryptography
|
||||
if(!ps.IsProtected) throw new SecurityException("ProtectedString-9");
|
||||
if(!ps2.IsProtected) throw new SecurityException("ProtectedString-10");
|
||||
|
||||
Random r = new Random();
|
||||
string str = string.Empty;
|
||||
ps = new ProtectedString();
|
||||
for(int i = 0; i < 100; ++i)
|
||||
@@ -497,16 +1063,26 @@ namespace ModernKeePassLib.Cryptography
|
||||
if(short.MinValue.ToString(NumberFormatInfo.InvariantInfo) !=
|
||||
"-32768")
|
||||
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
|
||||
}
|
||||
|
||||
private static void TestUrlUtil()
|
||||
{
|
||||
#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") !=
|
||||
"domain")
|
||||
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");
|
||||
if(UrlUtil.GetHost(@"mailto:bob@example.com") != "example.com")
|
||||
throw new InvalidOperationException("UrlUtil-H3");
|
||||
|
Reference in New Issue
Block a user