Setup solution

This commit is contained in:
Geoffroy BONNEVILLE
2019-07-25 16:39:43 +02:00
parent 81509be167
commit 1b2007e6dd
136 changed files with 35834 additions and 0 deletions

View File

@@ -0,0 +1,79 @@
using System.IO;
using System.Text;
using ModernKeePassLib.Serialization;
using ModernKeePassLib.Cryptography.Cipher;
using ModernKeePassLib.Utility;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Parameters;
using Xunit;
using System.Security.Cryptography;
using ModernKeePassLib.Cryptography;
namespace ModernKeePassLib.Test.Cryptography.Cipher
{
public class AesTests
{
// Test vector (official ECB test vector #356)
private readonly byte[] _pbReferenceCt =
{
0x75, 0xD1, 0x1B, 0x0E, 0x3A, 0x68, 0xC4, 0x22,
0x3D, 0x88, 0xDB, 0xF0, 0x17, 0x97, 0x7D, 0xD7
};
private readonly byte[] _pbIv = new byte[16];
private readonly byte[] _pbTestKey = new byte[32];
private readonly byte[] _pbTestData =
{
0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
[Fact]
public void TestEncryptStream()
{
var a = CryptoUtil.CreateAes();
if (a.BlockSize != 128) // AES block size
{
//Debug.Assert(false);
a.BlockSize = 128;
}
a.IV = _pbIv;
a.KeySize = 256;
a.Key = _pbTestKey;
a.Mode = CipherMode.ECB;
var iCrypt = a.CreateEncryptor();
iCrypt.TransformBlock(_pbTestData, 0, 16, _pbTestData, 0);
Assert.True(MemUtil.ArraysEqual(_pbTestData, _pbReferenceCt));
}
[Fact]
public void TestDecryptStream()
{
// Possible Mono Bug? This only works with size >= 48
using (var inStream = new MemoryStream(new byte[32]))
{
inStream.Write(_pbReferenceCt, 0, _pbReferenceCt.Length);
inStream.Position = 0;
var aes = new StandardAesEngine();
using (var outStream = aes.DecryptStream(inStream, _pbTestKey, _pbIv))
{
var outBytes = new BinaryReaderEx(outStream, Encoding.UTF8, string.Empty).ReadBytes(16);
Assert.True(MemUtil.ArraysEqual(outBytes, _pbTestData));
}
}
}
[Fact]
public void TestBouncyCastleAes()
{
var aesEngine = new AesEngine();
//var parametersWithIv = new ParametersWithIV(new KeyParameter(pbTestKey), pbIV);
aesEngine.Init(true, new KeyParameter(_pbTestKey));
Assert.Equal(_pbTestData.Length, aesEngine.GetBlockSize());
aesEngine.ProcessBlock(_pbTestData, 0, _pbTestData, 0);
Assert.True(MemUtil.ArraysEqual(_pbReferenceCt,_pbTestData));
}
}
}

View File

@@ -0,0 +1,203 @@
using System;
using System.Diagnostics;
using System.IO;
using ModernKeePassLib.Cryptography;
using ModernKeePassLib.Cryptography.Cipher;
using ModernKeePassLib.Utility;
using Xunit;
namespace ModernKeePassLib.Test.Cryptography.Cipher
{
public class Chacha20Tests
{
[Fact]
public void TestChacha20Cipher()
{
// ======================================================
// Test vector from RFC 7539, section 2.3.2
var pbKey = new byte[32];
for (var i = 0; i < 32; ++i) pbKey[i] = (byte)i;
var pbIV = new byte[12];
pbIV[3] = 0x09;
pbIV[7] = 0x4A;
var 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
};
var pb = new byte[64];
using (var c = new ChaCha20Cipher(pbKey, pbIV))
{
c.Seek(64, SeekOrigin.Begin); // Skip first block
c.Encrypt(pb, 0, pb.Length);
Assert.True(MemUtil.ArraysEqual(pb, pbExpc));
}
#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
};
var pb64 = new byte[64];
using (var c = new ChaCha20Cipher(pbKey, pbIV))
{
c.Encrypt(pb64, 0, pb64.Length); // Skip first block
c.Encrypt(pb, 0, pb.Length);
Assert.True(MemUtil.ArraysEqual(pb, pbExpc));
}
// ======================================================
// 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 (var msEnc = new MemoryStream())
{
using (var c = new ChaCha20Stream(msEnc, true, pbKey, pbIV))
{
var r = CryptoRandom.NewWeakRandom();
r.NextBytes(pb64);
c.Write(pb64, 0, pb64.Length); // Skip first block
var p = 0;
while (p < pb.Length)
{
var cb = r.Next(1, pb.Length - p + 1);
c.Write(pb, p, cb);
p += cb;
}
Debug.Assert(p == pb.Length);
}
var pbEnc0 = msEnc.ToArray();
var pbEnc = MemUtil.Mid(pbEnc0, 64, pbEnc0.Length - 64);
Assert.True(MemUtil.ArraysEqual(pbEnc, pbExpc));
using (var msCT = new MemoryStream(pbEnc0, false))
{
using (var cDec = new ChaCha20Stream(msCT, false,
pbKey, pbIV))
{
var pbPT = MemUtil.Read(cDec, pbEnc0.Length);
Assert.True(cDec.ReadByte() < 0);
Assert.True(MemUtil.ArraysEqual(MemUtil.Mid(pbPT, 0, 64), pb64));
Assert.True(MemUtil.ArraysEqual(MemUtil.Mid(pbPT, 64, pbEnc.Length), pb));
}
}
}
// ======================================================
// 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 (var c = new ChaCha20Cipher(pbKey, pbIV, true))
{
c.Decrypt(pb, 0, pb.Length);
Assert.True(MemUtil.ArraysEqual(pb, pbExpc));
}
#endif
}
}
}

View File

@@ -0,0 +1,84 @@
using System;
using System.Collections.Generic;
using ModernKeePassLib.Cryptography;
using ModernKeePassLib.Cryptography.Cipher;
using ModernKeePassLib.Utility;
using Xunit;
namespace ModernKeePassLib.Test.Cryptography.Cipher
{
public class Salsa20Tests
{
[Fact]
public void TestSalsa20Cipher()
{
var r = CryptoRandom.NewWeakRandom();
// Test values from official set 6, vector 3
var pbKey = new byte[] {
0x0F, 0x62, 0xB5, 0x08, 0x5B, 0xAE, 0x01, 0x54,
0xA7, 0xFA, 0x4D, 0xA0, 0xF3, 0x46, 0x99, 0xEC,
0x3F, 0x92, 0xE5, 0x38, 0x8B, 0xDE, 0x31, 0x84,
0xD7, 0x2A, 0x7D, 0xD0, 0x23, 0x76, 0xC9, 0x1C
};
var pbIv = new byte[] { 0x28, 0x8F, 0xF6, 0x5D,
0xC4, 0x2B, 0x92, 0xF9 };
var pbExpected = new byte[] {
0x5E, 0x5E, 0x71, 0xF9, 0x01, 0x99, 0x34, 0x03,
0x04, 0xAB, 0xB2, 0x2A, 0x37, 0xB6, 0x62, 0x5B
};
var pb = new byte[16];
var c = new Salsa20Cipher(pbKey, pbIv);
c.Encrypt(pb, 0, pb.Length);
Assert.True(MemUtil.ArraysEqual(pb, pbExpected));
// Extended test
var pbExpected2 = new byte[] {
0xAB, 0xF3, 0x9A, 0x21, 0x0E, 0xEE, 0x89, 0x59,
0x8B, 0x71, 0x33, 0x37, 0x70, 0x56, 0xC2, 0xFE
};
var pbExpected3 = new byte[] {
0x1B, 0xA8, 0x9D, 0xBD, 0x3F, 0x98, 0x83, 0x97,
0x28, 0xF5, 0x67, 0x91, 0xD5, 0xB7, 0xCE, 0x23
};
var nPos = Salsa20ToPos(c, r, pb.Length, 65536);
Array.Clear(pb, 0, pb.Length);
c.Encrypt(pb, 0, pb.Length);
Assert.True(MemUtil.ArraysEqual(pb, pbExpected2));
Salsa20ToPos(c, r, nPos + pb.Length, 131008);
Array.Clear(pb, 0, pb.Length);
c.Encrypt(pb, 0, pb.Length);
Assert.True(MemUtil.ArraysEqual(pb, pbExpected3));
var d = new Dictionary<string, bool>();
const int nRounds = 100;
for (var i = 0; i < nRounds; ++i)
{
var z = new byte[32];
c = new Salsa20Cipher(z, MemUtil.Int64ToBytes(i));
c.Encrypt(z, 0, z.Length);
d[MemUtil.ByteArrayToHexString(z)] = true;
}
Assert.Equal(nRounds, d.Count);
}
private static int Salsa20ToPos(Salsa20Cipher c, Random r, int nPos,
int nTargetPos)
{
var pb = new byte[512];
while (nPos < nTargetPos)
{
var x = r.Next(1, 513);
var nGen = Math.Min(nTargetPos - nPos, x);
c.Encrypt(pb, 0, nGen);
nPos += nGen;
}
return nTargetPos;
}
}
}

View File

@@ -0,0 +1,54 @@
using ModernKeePassLib.Cryptography;
using ModernKeePassLib.Utility;
using Xunit;
namespace ModernKeePassLib.Test.Cryptography
{
public class CryptoRandomStreamTests
{
private void TestGetRandomBytes(CryptoRandomStream stream)
{
const uint length = 16;
var bytes1 = stream.GetRandomBytes(length);
Assert.Equal(bytes1.Length, (int)length);
var bytes2 = stream.GetRandomBytes(length);
Assert.False(MemUtil.ArraysEqual(bytes2, bytes1));
}
[Fact]
public void TestGetRandomBytesCrsAlgorithmSalsa20()
{
var stream = new CryptoRandomStream(CrsAlgorithm.Salsa20, new byte[16]);
TestGetRandomBytes(stream);
}
[Fact]
public void TestGetRandomBytesCrsAlgorithmArcFourVariant()
{
var stream = new CryptoRandomStream(CrsAlgorithm.ArcFourVariant, new byte[16]);
TestGetRandomBytes(stream);
}
private void TestGetRandomInt64(CryptoRandomStream stream)
{
var value1 = stream.GetRandomUInt64();
var value2 = stream.GetRandomUInt64();
Assert.NotEqual(value2, value1);
}
[Fact]
public void TestGetRandomInt64AlgorithmSalsa20()
{
var stream = new CryptoRandomStream(CrsAlgorithm.Salsa20, new byte[16]);
TestGetRandomInt64(stream);
}
[Fact]
public void TestGetRandomInt64AlgorithmArcFourVariant()
{
var stream = new CryptoRandomStream(CrsAlgorithm.ArcFourVariant, new byte[16]);
TestGetRandomInt64(stream);
}
}
}

View File

@@ -0,0 +1,37 @@
using ModernKeePassLib.Cryptography;
using Xunit;
namespace ModernKeePassLib.Test.Cryptography
{
public class CryptoRandomTests
{
[Fact]
public void TestAddEntropy()
{
// just making sure it does not throw an exception
CryptoRandom.Instance.AddEntropy(new byte[1]);
}
[Fact]
public void TestGetRandomBytes()
{
const int length = 32;
var bytes1 = CryptoRandom.Instance.GetRandomBytes(length);
Assert.Equal(bytes1.Length, length);
var bytes2 = CryptoRandom.Instance.GetRandomBytes(length);
Assert.NotEqual(bytes2, bytes1);
}
[Fact]
public void TestGeneratedBytesCount()
{
const int length = 1;
CryptoRandom.Instance.GetRandomBytes(length);
var count1 = CryptoRandom.Instance.GeneratedBytesCount;
CryptoRandom.Instance.GetRandomBytes(length);
var count2 = CryptoRandom.Instance.GeneratedBytesCount;
Assert.True(count2 > count1);
}
}
}

View File

@@ -0,0 +1,100 @@
using System;
using System.Text;
using ModernKeePassLib.Cryptography;
using ModernKeePassLib.Cryptography.Hash;
using ModernKeePassLib.Utility;
using Xunit;
namespace ModernKeePassLib.Test.Cryptography.Hash
{
public class Blake2bTests
{
[Fact]
public void TestBlake2bUtf8()
{
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);
Assert.True(MemUtil.ArraysEqual(pbC, pbExpc));
}
[Fact]
public void TestBlake2bEmpty()
{
// ======================================================
// Computed using the official b2sum tool
Blake2b h = new Blake2b();
var 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
};
var pbC = h.ComputeHash(new byte[0]);
Assert.True(MemUtil.ArraysEqual(pbC, pbExpc));
}
[Fact]
public void TestBlake2bString()
{
// ======================================================
// Computed using the official b2sum tool
Blake2b h = new Blake2b();
string strS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.:,;_-\r\n";
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 1000; ++i) sb.Append(strS);
var pbData = StrUtil.Utf8.GetBytes(sb.ToString());
var 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
};
Random r = CryptoRandom.NewWeakRandom();
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;
}
Assert.Equal(p, pbData.Length);
h.TransformFinalBlock(new byte[0], 0, 0);
Assert.True(MemUtil.ArraysEqual(h.Hash, pbExpc));
h.Clear();
}
}
}

View File

@@ -0,0 +1,116 @@
using ModernKeePassLib.Cryptography;
using ModernKeePassLib.Utility;
using System.Security.Cryptography;
using Xunit;
namespace ModernKeePassLib.Test.Cryptography.Hash
{
public class HmacTests
{
[Fact]
public void TestHmac1()
{
// Test vectors from RFC 4231
var pbKey = new byte[20];
for (var i = 0; i < pbKey.Length; ++i) pbKey[i] = 0x0B;
var pbMsg = StrUtil.Utf8.GetBytes("Hi There");
var pbExpc = new byte[]
{
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);
}
[Fact]
public void TestHmac2()
{
var pbKey = new byte[131];
for (var i = 0; i < pbKey.Length; ++i) pbKey[i] = 0xAA;
var 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.");
var pbExpc = new byte[] {
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);
}
[Fact]
public void TestHmacSha1ComputeHash()
{
var expectedHash = "AC2C2E614882CE7158F69B7E3B12114465945D01";
var message = StrUtil.Utf8.GetBytes("testing123");
var key = StrUtil.Utf8.GetBytes("hello");
using (var result = new HMACSHA1(key))
{
Assert.Equal(ByteToString(result.ComputeHash(message)), expectedHash);
}
}
[Fact]
public void TestHmacSha256ComputeHash()
{
var expectedHash = "09C1BD2DE4E5659C0EFAF9E6AE4723E9CF96B69609B4E562F6AFF1745D7BF4E0";
var message = StrUtil.Utf8.GetBytes("testing123");
var key = StrUtil.Utf8.GetBytes("hello");
using (var result = new HMACSHA256(key))
{
Assert.Equal(ByteToString(result.ComputeHash(message)), expectedHash);
}
}
private static string ByteToString(byte[] buff)
{
string sbinary = "";
for (int i = 0; i < buff.Length; i++)
{
sbinary += buff[i].ToString("X2"); // hex format
}
return (sbinary);
}
[Fact]
public void TestHmacOtp()
{
var pbSecret = StrUtil.Utf8.GetBytes("12345678901234567890");
var vExp = new []{ "755224", "287082", "359152",
"969429", "338314", "254676", "287922", "162583", "399871",
"520489" };
for (var i = 0; i < vExp.Length; ++i)
{
Assert.Equal(HmacOtp.Generate(pbSecret, (ulong)i, 6, false, -1), vExp[i]);
}
}
private static void HmacEval(byte[] pbKey, byte[] pbMsg,
byte[] pbExpc)
{
using (var h = new HMACSHA256(pbKey))
{
h.TransformBlock(pbMsg, 0, pbMsg.Length, pbMsg, 0);
h.TransformFinalBlock(new byte[0], 0, 0);
byte[] pbHash = h.Hash;
Assert.True(MemUtil.ArraysEqual(pbHash, pbExpc));
// Reuse the object
h.Initialize();
h.TransformBlock(pbMsg, 0, pbMsg.Length, pbMsg, 0);
h.TransformFinalBlock(new byte[0], 0, 0);
pbHash = h.Hash;
Assert.True(MemUtil.ArraysEqual(pbHash, pbExpc));
}
}
}
}

View File

@@ -0,0 +1,73 @@
using ModernKeePassLib.Utility;
using System.Security.Cryptography;
using ModernKeePassLib.Cryptography;
using Xunit;
namespace ModernKeePassLib.Test.Cryptography.Hash
{
public class ShaManagedTests
{
[Fact]
public void TestSha256()
{
var r = CryptoRandom.NewWeakRandom();
var pbData = new byte[517];
r.NextBytes(pbData);
byte[] pbH1;
using (var h1 = new SHA256Managed())
{
var i = 0;
while (i != pbData.Length)
{
var cb = r.Next(pbData.Length - i) + 1;
h1.TransformBlock(pbData, i, cb, pbData, i);
i += cb;
}
h1.TransformFinalBlock(MemUtil.EmptyByteArray, 0, 0);
pbH1 = h1.Hash;
}
byte[] pbH2;
using (var h2 = new SHA256Managed())
{
pbH2 = h2.ComputeHash(pbData);
}
Assert.True(MemUtil.ArraysEqual(pbH1, pbH2));
}
[Fact]
public void TestSha256ComputeHash()
{
var expectedHash = "B822F1CD2DCFC685B47E83E3980289FD5D8E3FF3A82DEF24D7D1D68BB272EB32";
var message = StrUtil.Utf8.GetBytes("testing123");
using (var result = new SHA256Managed())
{
Assert.Equal(ByteToString(result.ComputeHash(message)), expectedHash);
}
}
[Fact]
public void TestSha512ComputeHash()
{
var expectedHash = "4120117B3190BA5E24044732B0B09AA9ED50EB1567705ABCBFA78431A4E0A96B1152ED7F4925966B1C82325E186A8100E692E6D2FCB6702572765820D25C7E9E";
var message = StrUtil.Utf8.GetBytes("testing123");
using (var result = new SHA512Managed())
{
Assert.Equal(ByteToString(result.ComputeHash(message)), expectedHash);
}
}
private static string ByteToString(byte[] buff)
{
var sbinary = "";
for (var i = 0; i < buff.Length; i++)
{
sbinary += buff[i].ToString("X2"); // hex format
}
return (sbinary);
}
}
}

View File

@@ -0,0 +1,81 @@
using System.IO;
using ModernKeePassLib.Cryptography;
using ModernKeePassLib.Utility;
using Xunit;
namespace ModernKeePassLib.Test.Cryptography
{
public class HashingStreamExTests
{
const string data = "test";
// The expected hash includes the \n added by WriteLine
static readonly byte[] sha256HashOfData =
{
0xf2, 0xca, 0x1b, 0xb6, 0xc7, 0xe9, 0x07, 0xd0,
0x6d, 0xaf, 0xe4, 0x68, 0x7e, 0x57, 0x9f, 0xce,
0x76, 0xb3, 0x7e, 0x4e, 0x93, 0xb7, 0x60, 0x50,
0x22, 0xda, 0x52, 0xe6, 0xcc, 0xc2, 0x6f, 0xd2
};
[Fact]
public void TestRead()
{
// if we use larger size, StreamReader will read past newline and cause bad hash
var bytes = new byte[data.Length + 1];
using (var ms = new MemoryStream(bytes))
{
using (var sw = new StreamWriter(ms))
{
// set NewLine to ensure we don't run into cross-platform issues on Windows
sw.NewLine = "\n";
sw.WriteLine(data);
}
}
using (var ms = new MemoryStream(bytes))
{
using (var hs = new HashingStreamEx(ms, false, null))
{
using (var sr = new StreamReader(hs))
{
var read = sr.ReadLine();
Assert.Equal(read, data);
}
// When the StreamReader is disposed, it calls Dispose on the
//HasingStreamEx, which computes the hash.
Assert.True(MemUtil.ArraysEqual(hs.Hash, sha256HashOfData));
}
}
}
[Fact]
public void TestWrite()
{
var bytes = new byte[16];
using (var ms = new MemoryStream(bytes))
{
using (var hs = new HashingStreamEx(ms, true, null))
{
using (var sw = new StreamWriter(hs))
{
// set NewLine to ensure we don't run into cross-platform issues on Windows
sw.NewLine = "\n";
sw.WriteLine(data);
}
// When the StreamWriter is disposed, it calls Dispose on the
//HasingStreamEx, which computes the hash.
Assert.True(MemUtil.ArraysEqual(hs.Hash, sha256HashOfData));
}
}
using (var ms = new MemoryStream(bytes))
{
using (var sr = new StreamReader(ms))
{
var read = sr.ReadLine();
Assert.Equal(read, data);
}
}
}
}
}

View File

@@ -0,0 +1,31 @@
using System.Text;
using ModernKeePassLib.Cryptography;
using Xunit;
namespace ModernKeePassLib.Test.Cryptography
{
public class HmacOtpTests
{
// Using the test case from Appendix D of RFC 4226
const string secret = "12345678901234567890";
static readonly string[] expectedHOTP = new string[]
{
"755224", "287082", "359152", "969429", "338314",
"254676", "287922", "162583", "399871", "520489"
};
[Fact]
public void TestGenerate()
{
var secretBytes = Encoding.UTF8.GetBytes(secret);
for (ulong i = 0; i < 10; i++)
{
var hotp = HmacOtp.Generate(secretBytes, i, 6, false, -1);
Assert.Equal(hotp, expectedHOTP[i]);
}
}
}
}

View File

@@ -0,0 +1,41 @@
using System;
using ModernKeePassLib.Cryptography;
using ModernKeePassLib.Cryptography.KeyDerivation;
using ModernKeePassLib.Utility;
using Xunit;
namespace ModernKeePassLib.Test.Cryptography.KeyDerivation
{
public class AesKdfTests
{
[Fact]
public void TestAesKdf()
{
// 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
var r = CryptoRandom.NewWeakRandom();
var pbKey = new byte[32];
r.NextBytes(pbKey);
var pbSeed = new byte[32];
r.NextBytes(pbSeed);
var uRounds = (ulong)r.Next(1, 0x7FFF);
var pbMan = new byte[pbKey.Length];
Array.Copy(pbKey, pbMan, pbKey.Length);
Assert.True(AesKdf.TransformKeyManaged(pbMan, pbSeed, uRounds));
pbMan = CryptoUtil.HashSha256(pbMan);
var kdf = new AesKdf();
var p = kdf.GetDefaultParameters();
p.SetUInt64(AesKdf.ParamRounds, uRounds);
p.SetByteArray(AesKdf.ParamSeed, pbSeed);
var pbKdf = kdf.Transform(pbKey, p);
Assert.True(MemUtil.ArraysEqual(pbMan, pbKdf));
}
}
}

View File

@@ -0,0 +1,145 @@
using ModernKeePassLib.Cryptography.KeyDerivation;
using ModernKeePassLib.Utility;
using Xunit;
namespace ModernKeePassLib.Test.Cryptography.KeyDerivation
{
public class Argon2Tests
{
[Fact]
public void TestArgon2()
{
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);
Assert.Equal(0x13U, p.GetUInt32(Argon2Kdf.ParamVersion, 0));
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);
Assert.True(MemUtil.ArraysEqual(pb, pbExpc));
// ======================================================
// 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);
Assert.True(MemUtil.ArraysEqual(pb, pbExpc));
// ======================================================
// 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);
Assert.True(MemUtil.ArraysEqual(pb, pbExpc));
// ======================================================
// 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);
Assert.True(MemUtil.ArraysEqual(pb, pbExpc));
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);
Assert.True(MemUtil.ArraysEqual(pb, pbExpc));
// TODO: Out of memory exception
/*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);
Assert.IsTrue(MemUtil.ArraysEqual(pb, pbExpc));*/
}
}
}

View File

@@ -0,0 +1,34 @@
using ModernKeePassLib.Cryptography.KeyDerivation;
using ModernKeePassLib.Keys;
using ModernKeePassLib.Utility;
using Xunit;
namespace ModernKeePassLib.Test.Keys
{
public class CompositeKeyTests
{
[Fact]
public void TestGenerateKey32()
{
var originalKey = new byte[32];
var expectedKey = new byte[]
{
0xF0, 0xED, 0x57, 0xD5, 0xF0, 0xDA, 0xF3, 0x47,
0x90, 0xD0, 0xDB, 0x43, 0x25, 0xC6, 0x81, 0x2C,
0x81, 0x6A, 0x0D, 0x94, 0x96, 0xA9, 0x03, 0xE1,
0x20, 0xD4, 0x3A, 0x3E, 0x45, 0xAD, 0x02, 0x65
};
const ulong rounds = 1;
var composite = new CompositeKey();
AesKdf kdf = new AesKdf();
KdfParameters p = kdf.GetDefaultParameters();
p.SetUInt64(AesKdf.ParamRounds, rounds);
p.SetByteArray(AesKdf.ParamSeed, originalKey);
var key = composite.GenerateKey32(p);
Assert.NotNull(key);
var keyData = key.ReadData();
Assert.True(MemUtil.ArraysEqual(keyData, expectedKey));
}
}
}

View File

@@ -0,0 +1,34 @@
using ModernKeePassLib.Keys;
using ModernKeePassLib.Utility;
using Xunit;
namespace ModernKeePassLib.Test.Keys
{
public class KcpCustomKeyTests
{
static readonly byte[] testData =
{
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
[Fact]
public void TestConstruct()
{
var expectedHash = new byte[32]
{
0xAF, 0x55, 0x70, 0xF5, 0xA1, 0x81, 0x0B, 0x7A,
0xF7, 0x8C, 0xAF, 0x4B, 0xC7, 0x0A, 0x66, 0x0F,
0x0D, 0xF5, 0x1E, 0x42, 0xBA, 0xF9, 0x1D, 0x4D,
0xE5, 0xB2, 0x32, 0x8D, 0xE0, 0xE8, 0x3D, 0xFC
};
var key = new KcpCustomKey("test1", testData, false);
var keyData = key.KeyData.ReadData();
Assert.True(MemUtil.ArraysEqual(keyData, testData));
key = new KcpCustomKey("test2", testData, true);
keyData = key.KeyData.ReadData();
Assert.True(MemUtil.ArraysEqual(keyData, expectedHash));
}
}
}

View File

@@ -0,0 +1,82 @@
using System;
using System.IO;
using ModernKeePassLib.Keys;
using ModernKeePassLib.Utility;
using Windows.Storage;
using Xunit;
namespace ModernKeePassLib.Test.Keys
{
public class KcpKeyFileTests
{
private const string TestCreateFile = "TestCreate.xml";
private const string TestKey = "0123456789";
private const string ExpectedFileStart =
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n" +
"<KeyFile>\r\n" +
"\t<Meta>\r\n" +
"\t\t<Version>1.00</Version>\r\n" +
"\t</Meta>\r\n" +
"\t<Key>\r\n" +
"\t\t<Data>";
private const string ExpectedFileEnd = "</Data>\r\n\t</Key>\r\n</KeyFile>";
[Fact]
public void TestConstruct()
{
var expectedKeyData = new byte[]
{
0x95, 0x94, 0xdc, 0xb9, 0x91, 0xc6, 0x65, 0xa0,
0x81, 0xf6, 0x6f, 0xca, 0x07, 0x1a, 0x30, 0xd1,
0x1d, 0x65, 0xcf, 0x8d, 0x9c, 0x60, 0xfb, 0xe6,
0x45, 0xfc, 0xc8, 0x92, 0xbd, 0xeb, 0xaf, 0xc3
};
var folder = StorageFolder.GetFolderFromPathAsync(Path.GetTempPath()).GetAwaiter().GetResult();
var file = folder.CreateFileAsync(TestCreateFile, CreationCollisionOption.ReplaceExisting).GetAwaiter().GetResult();
using (var fs = file.OpenStreamForWriteAsync().GetAwaiter().GetResult())
{
using (var sw = new StreamWriter(fs))
{
sw.Write(ExpectedFileStart);
sw.Write(TestKey);
sw.Write(ExpectedFileEnd);
}
}
try
{
var keyFile = new KcpKeyFile(file);
var keyData = keyFile.KeyData.ReadData();
Assert.True(MemUtil.ArraysEqual(keyData, expectedKeyData));
}
finally
{
file.DeleteAsync().GetAwaiter().GetResult();
}
}
[Fact]
public void TestCreate()
{
var folder = StorageFolder.GetFolderFromPathAsync(Path.GetTempPath()).GetAwaiter().GetResult();
var file = folder.CreateFileAsync(TestCreateFile, CreationCollisionOption.ReplaceExisting).GetAwaiter().GetResult();
KcpKeyFile.Create(file, null);
try
{
var fileContents = FileIO.ReadTextAsync(file).GetAwaiter().GetResult();
Assert.Equal(185, fileContents.Length);
Assert.StartsWith(ExpectedFileStart, fileContents);
Assert.EndsWith(ExpectedFileEnd, fileContents);
}
finally
{
file.DeleteAsync().GetAwaiter().GetResult();
}
}
}
}

View File

@@ -0,0 +1,28 @@
using ModernKeePassLib.Keys;
using ModernKeePassLib.Utility;
using Xunit;
namespace ModernKeePassLib.Test.Keys
{
public class KcpPasswordTests
{
const string testPassword = "password";
[Fact]
public void TestConstruct()
{
var expectedHash = new byte[32]
{
0x5E, 0x88, 0x48, 0x98, 0xDA, 0x28, 0x04, 0x71,
0x51, 0xD0, 0xE5, 0x6F, 0x8D, 0xC6, 0x29, 0x27,
0x73, 0x60, 0x3D, 0x0D, 0x6A, 0xAB, 0xBD, 0xD6,
0x2A, 0x11, 0xEF, 0x72, 0x1D, 0x15, 0x42, 0xD8
};
var key = new KcpPassword(testPassword);
var keyData = key.KeyData.ReadData();
Assert.True(MemUtil.ArraysEqual(keyData, expectedHash));
}
}
}

View File

@@ -0,0 +1,31 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.0</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.2.0" />
<PackageReference Include="Portable.BouncyCastle" Version="1.8.5" />
<PackageReference Include="System.Runtime.WindowsRuntime" Version="4.3.0" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ModernKeePassLib\ModernKeePassLib.csproj" />
</ItemGroup>
<ItemGroup>
<Reference Include="Windows">
<HintPath>..\ModernKeePassLib\Libs\Windows.winmd</HintPath>
<IsWinMDFile>true</IsWinMDFile>
</Reference>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,110 @@
using System.Text;
using ModernKeePassLib.Cryptography;
using ModernKeePassLib.Security;
using ModernKeePassLib.Utility;
using Xunit;
namespace ModernKeePassLib.Test.Security
{
public class ProtectedObjectsTests
{
private readonly Encoding _enc = StrUtil.Utf8;
[Fact]
public void TestAreBinaryObjectsProtected()
{
var pbData = _enc.GetBytes("Test Test Test Test");
var pb = new ProtectedBinary(true, pbData);
Assert.True(pb.IsProtected);
var pbDec = pb.ReadData();
Assert.True(MemUtil.ArraysEqual(pbData, pbDec));
Assert.True(pb.IsProtected);
var pbData2 = _enc.GetBytes("Test Test Test Test");
var pbData3 = _enc.GetBytes("Test Test Test Test Test");
var pb2 = new ProtectedBinary(true, pbData2);
var pb3 = new ProtectedBinary(true, pbData3);
Assert.True(pb.Equals(pb2));
Assert.False(pb.Equals(pb3));
Assert.False(pb2.Equals(pb3));
Assert.Equal(pb.GetHashCode(), pb2.GetHashCode());
Assert.True(pb.Equals((object) pb2));
Assert.False(pb.Equals((object) pb3));
Assert.False(pb2.Equals((object) pb3));
}
[Fact]
public void TestIsEmptyProtectedStringEmpty()
{
var ps = new ProtectedString();
Assert.Equal(0, ps.Length);
Assert.True(ps.IsEmpty);
Assert.Equal(0, ps.ReadString().Length);
}
[Fact]
public void TestAreEqualStringsProtected()
{
var ps = new ProtectedString(true, "Test");
var ps2 = new ProtectedString(true, _enc.GetBytes("Test"));
Assert.False(ps.IsEmpty);
var pbData = ps.ReadUtf8();
var pbData2 = ps2.ReadUtf8();
Assert.True(MemUtil.ArraysEqual(pbData, pbData2));
Assert.Equal(4, pbData.Length);
Assert.Equal(ps.ReadString(), ps2.ReadString());
pbData = ps.ReadUtf8();
pbData2 = ps2.ReadUtf8();
Assert.True(MemUtil.ArraysEqual(pbData, pbData2));
Assert.True(ps.IsProtected);
Assert.True(ps2.IsProtected);
}
[Fact]
public void TestIsRandomStringProtected()
{
var r = CryptoRandom.NewWeakRandom();
var str = string.Empty;
var ps = new ProtectedString();
for (var i = 0; i < 100; ++i)
{
var bProt = ((r.Next() % 4) != 0);
ps = ps.WithProtection(bProt);
var x = r.Next(str.Length + 1);
var c = r.Next(20);
var ch = (char) r.Next(1, 256);
var strIns = new string(ch, c);
str = str.Insert(x, strIns);
ps = ps.Insert(x, strIns);
Assert.Equal(bProt, ps.IsProtected);
Assert.Equal(str, ps.ReadString());
ps = ps.WithProtection(bProt);
x = r.Next(str.Length);
c = r.Next(str.Length - x + 1);
str = str.Remove(x, c);
ps = ps.Remove(x, c);
Assert.Equal(bProt, ps.IsProtected);
Assert.Equal(str, ps.ReadString());
}
}
[Fact]
public void TestAreConcatenatedStringsProtected()
{
var ps = new ProtectedString(false, "ABCD");
var ps2 = new ProtectedString(true, "EFG");
ps += (ps2 + "HI");
Assert.True(ps.Equals(new ProtectedString(true, "ABCDEFGHI"), true));
Assert.True(ps.Equals(new ProtectedString(false, "ABCDEFGHI"), false));
}
}
}

View File

@@ -0,0 +1,72 @@
using System.IO;
using ModernKeePassLib.Serialization;
using ModernKeePassLib.Utility;
using Xunit;
namespace ModernKeePassLib.Test.Serialization
{
public class HashedBlockStreamTests
{
static readonly byte[] data = new byte[16];
static readonly byte[] hashStreamData = new byte[]
{
// The first 4 bytes are an integer indicating the block index
0x00, 0x00, 0x00, 0x00,
// Then the SHA-256 hash of the data
0x37, 0x47, 0x08, 0xFF, 0xF7, 0x71, 0x9D, 0xD5,
0x97, 0x9E, 0xC8, 0x75, 0xD5, 0x6C, 0xD2, 0x28,
0x6F, 0x6D, 0x3C, 0xF7, 0xEC, 0x31, 0x7A, 0x3B,
0x25, 0x63, 0x2A, 0xAB, 0x28, 0xEC, 0x37, 0xBB,
// then an integer that is the length of the data
0x10, 0x00, 0x00, 0x00,
// and finally the data itself
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// Next, a terminating block
0x01, 0x00, 0x00, 0x00,
// terminating block is indicated by a hash of all 0s...
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// ...and by a size of 0
0x00, 0x00, 0x00, 0x00
};
[Fact]
public void TestRead()
{
using (var ms = new MemoryStream(hashStreamData))
{
using (var hbs = new HashedBlockStream(ms, false))
{
using (var br = new BinaryReader(hbs))
{
var bytes = br.ReadBytes(data.Length);
Assert.True(MemUtil.ArraysEqual(bytes, data));
Assert.Throws<EndOfStreamException>(() => br.ReadByte());
}
}
}
}
[Fact]
public void TestWrite()
{
var buffer = new byte[hashStreamData.Length];
using (var ms = new MemoryStream(buffer))
{
using (var hbs = new HashedBlockStream(ms, true))
{
using (var bw = new BinaryWriter(hbs))
{
bw.Write(data);
}
}
Assert.True(MemUtil.ArraysEqual(buffer, hashStreamData));
}
}
}
}

View File

@@ -0,0 +1,170 @@
using System;
using System.Globalization;
using System.IO;
using System.Text;
using ModernKeePassLib.Keys;
using ModernKeePassLib.Security;
using ModernKeePassLib.Serialization;
using ModernKeePassLib.Collections;
using Xunit;
namespace ModernKeePassLib.Test.Serialization
{
public class KdbxFileTests
{
const string TestLocalizedAppName = "My Localized App Name";
const string TestDatabaseName = "My Database Name";
const string TestDatabaseDescription = "My Database Description";
const string TestDefaultUserName = "My Default User Name";
const string TestColor = "#FF0000"; // Red
const string TestRootGroupName = "My Root Group Name";
const string TestRootGroupNotes = "My Root Group Notes";
const string TestRootGroupDefaultAutoTypeSequence = "My Root Group Default Auto Type Sequence";
const string TestDatabase = "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\"?>\r\n" +
"<KeePassFile>\r\n" +
"\t<Meta>\r\n" +
"\t\t<Generator>" + TestLocalizedAppName + "</Generator>\r\n" +
"\t\t<DatabaseName>" + TestDatabaseName + "</DatabaseName>\r\n" +
"\t\t<DatabaseNameChanged>2017-10-23T08:03:55Z</DatabaseNameChanged>\r\n" +
"\t\t<DatabaseDescription>" + TestDatabaseDescription + "</DatabaseDescription>\r\n" +
"\t\t<DatabaseDescriptionChanged>2017-10-23T08:03:55Z</DatabaseDescriptionChanged>\r\n" +
"\t\t<DefaultUserName>" + TestDefaultUserName + "</DefaultUserName>\r\n" +
"\t\t<DefaultUserNameChanged>2017-10-23T08:03:55Z</DefaultUserNameChanged>\r\n" +
"\t\t<MaintenanceHistoryDays>365</MaintenanceHistoryDays>\r\n" +
//"\t\t<Color>" + testColor + "</Color>\r\n" +
"\t\t<Color></Color>\r\n" +
"\t\t<MasterKeyChanged>2017-10-23T08:03:55Z</MasterKeyChanged>\r\n" +
"\t\t<MasterKeyChangeRec>-1</MasterKeyChangeRec>\r\n" +
"\t\t<MasterKeyChangeForce>-1</MasterKeyChangeForce>\r\n" +
"\t\t<MemoryProtection>\r\n" +
"\t\t\t<ProtectTitle>False</ProtectTitle>\r\n" +
"\t\t\t<ProtectUserName>False</ProtectUserName>\r\n" +
"\t\t\t<ProtectPassword>True</ProtectPassword>\r\n" +
"\t\t\t<ProtectURL>False</ProtectURL>\r\n" +
"\t\t\t<ProtectNotes>False</ProtectNotes>\r\n" +
"\t\t</MemoryProtection>\r\n" +
"\t\t<RecycleBinEnabled>True</RecycleBinEnabled>\r\n" +
"\t\t<RecycleBinUUID>AAAAAAAAAAAAAAAAAAAAAA==</RecycleBinUUID>\r\n" +
"\t\t<RecycleBinChanged>2017-10-23T08:03:55Z</RecycleBinChanged>\r\n" +
"\t\t<EntryTemplatesGroup>AAAAAAAAAAAAAAAAAAAAAA==</EntryTemplatesGroup>\r\n" +
"\t\t<EntryTemplatesGroupChanged>2017-10-23T08:03:55Z</EntryTemplatesGroupChanged>\r\n" +
"\t\t<HistoryMaxItems>10</HistoryMaxItems>\r\n" +
"\t\t<HistoryMaxSize>6291456</HistoryMaxSize>\r\n" +
"\t\t<LastSelectedGroup>AAAAAAAAAAAAAAAAAAAAAA==</LastSelectedGroup>\r\n" +
"\t\t<LastTopVisibleGroup>AAAAAAAAAAAAAAAAAAAAAA==</LastTopVisibleGroup>\r\n" +
"\t\t<Binaries />\r\n" +
"\t\t<CustomData />\r\n" +
"\t</Meta>\r\n" +
"\t<Root>\r\n" +
"\t\t<Group>\r\n" +
"\t\t\t<UUID>AAAAAAAAAAAAAAAAAAAAAA==</UUID>\r\n" +
"\t\t\t<Name>" + TestRootGroupName + "</Name>\r\n" +
"\t\t\t<Notes>" + TestRootGroupNotes + "</Notes>\r\n" +
"\t\t\t<IconID>49</IconID>\r\n" +
"\t\t\t<Times>\r\n" +
"\t\t\t\t<CreationTime>2017-10-23T08:03:55Z</CreationTime>\r\n" +
"\t\t\t\t<LastModificationTime>2017-10-23T08:03:55Z</LastModificationTime>\r\n" +
"\t\t\t\t<LastAccessTime>2017-10-23T08:03:55Z</LastAccessTime>\r\n" +
"\t\t\t\t<ExpiryTime>2017-10-23T08:03:55Z</ExpiryTime>\r\n" +
"\t\t\t\t<Expires>False</Expires>\r\n" +
"\t\t\t\t<UsageCount>0</UsageCount>\r\n" +
"\t\t\t\t<LocationChanged>2017-10-23T08:03:55Z</LocationChanged>\r\n" +
"\t\t\t</Times>\r\n" +
"\t\t\t<IsExpanded>True</IsExpanded>\r\n" +
"\t\t\t<DefaultAutoTypeSequence>" + TestRootGroupDefaultAutoTypeSequence + "</DefaultAutoTypeSequence>\r\n" +
"\t\t\t<EnableAutoType>null</EnableAutoType>\r\n" +
"\t\t\t<EnableSearching>null</EnableSearching>\r\n" +
"\t\t\t<LastTopVisibleEntry>AAAAAAAAAAAAAAAAAAAAAA==</LastTopVisibleEntry>\r\n" +
"\t\t</Group>\r\n" +
"\t\t<DeletedObjects />\r\n" +
"\t</Root>\r\n" +
"</KeePassFile>";
const string TestDate = "2017-10-23T08:03:55Z";
[Fact]
public void TestLoad()
{
var database = new PwDatabase();
using (var ms = new MemoryStream(Encoding.UTF8.GetBytes(TestDatabase)))
{
var file = new KdbxFile(database);
file.Load(ms, KdbxFormat.PlainXml, null);
}
//Assert.That(database.Color.ToArgb(), Is.EqualTo(Color.Red.ToArgb()));
Assert.Equal(PwCompressionAlgorithm.GZip, database.Compression);
//Assert.That (database.CustomData, Is.EqualTo ());
Assert.True(database.CustomIcons.Count == 0);
}
[Fact]
public void TestSave()
{
var buffer = new byte[4096];
using (var ms = new MemoryStream(buffer))
{
var database = new PwDatabase();
database.New(new IOConnectionInfo(), new CompositeKey());
var date = DateTime.Parse(TestDate, CultureInfo.CurrentCulture, DateTimeStyles.AdjustToUniversal);
//var date = DateTime.UtcNow;
PwDatabase.LocalizedAppName = TestLocalizedAppName;
database.Name = TestDatabaseName;
database.NameChanged = date;
database.Description = TestDatabaseDescription;
database.DescriptionChanged = date;
database.DefaultUserName = TestDefaultUserName;
database.DefaultUserNameChanged = date;
//database.Color = Color.Red;
database.MasterKeyChanged = date;
database.RecycleBinChanged = date;
database.EntryTemplatesGroupChanged = date;
database.RootGroup.Uuid = PwUuid.Zero;
database.RootGroup.Name = TestRootGroupName;
database.RootGroup.Notes = TestRootGroupNotes;
database.RootGroup.DefaultAutoTypeSequence = TestRootGroupDefaultAutoTypeSequence;
database.RootGroup.CreationTime = date;
database.RootGroup.LastModificationTime = date;
database.RootGroup.LastAccessTime = date;
database.RootGroup.ExpiryTime = date;
database.RootGroup.LocationChanged = date;
var file = new KdbxFile(database);
file.Save(ms, null, KdbxFormat.PlainXml, null);
}
var fileContents = Encoding.UTF8.GetString(buffer, 0, buffer.Length).Replace("\0", "");
if (typeof(KdbxFile).Namespace.StartsWith("KeePassLib."))
{
// Upstream KeePassLib does not specify line endings for XmlTextWriter,
// so it uses native line endings.
fileContents = fileContents.Replace("\n", "\r\n");
}
Assert.Equal(fileContents, TestDatabase);
}
[Fact]
public void TestSearch()
{
var database = new PwDatabase();
using (var ms = new MemoryStream(Encoding.UTF8.GetBytes(TestDatabase)))
{
var file = new KdbxFile(database);
file.Load(ms, KdbxFormat.PlainXml, null);
}
var sp = new SearchParameters()
{
SearchString = "sfsoiwsefsi"
};
var listStorage = new PwObjectList<PwEntry>();
database.RootGroup.SearchEntries(sp, listStorage);
Assert.Equal(0U, listStorage.UCount);
var entry = new PwEntry(true, true);
entry.Strings.Set("Title", new ProtectedString(false, "NaMe"));
database.RootGroup.AddEntry(entry, true);
sp.SearchString = "name";
database.RootGroup.SearchEntries(sp, listStorage);
Assert.Equal(1U, listStorage.UCount);
}
}
}

View File

@@ -0,0 +1,26 @@
using System;
using ModernKeePassLib.Utility;
using Xunit;
namespace ModernKeePassLib.Test.Utility
{
public class GfxUtilTests
{
// 16x16 all white PNG file, base64 encoded
const string testImageData =
"iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAACXBIWXMAAAsTAAA" +
"LEwEAmpwYAAAAB3RJTUUH3wMOFgIgmTCUMQAAABl0RVh0Q29tbWVudABDcmVhdG" +
"VkIHdpdGggR0lNUFeBDhcAAAAaSURBVCjPY/z//z8DKYCJgUQwqmFUw9DRAABVb" +
"QMdny4VogAAAABJRU5ErkJggg==";
//[Fact]
//public void TestLoadImage ()
//{
// var testData = Convert.FromBase64String (testImageData);
// var image = GfxUtil.ScaleImage(testData, 16, 16, ScaleTransformFlags.UIIcon);
// //var image = GfxUtil.LoadImage(testData);
// Assert.Equal(image.Width, 16);
// Assert.Equal(image.Height, 16);
//}
}
}

View File

@@ -0,0 +1,88 @@
using System.Text;
using ModernKeePassLib.Cryptography;
using ModernKeePassLib.Utility;
using Xunit;
namespace ModernKeePassLib.Test.Utility
{
public class MemUtilTests
{
private byte[] _pb = CryptoRandom.Instance.GetRandomBytes((uint)CryptoRandom.NewWeakRandom().Next(0, 0x2FFFF));
[Fact]
public void TestGzip()
{
var pbCompressed = MemUtil.Compress(_pb);
Assert.True(MemUtil.ArraysEqual(MemUtil.Decompress(pbCompressed), _pb));
}
[Fact]
public void TestMemUtil()
{
var enc = StrUtil.Utf8;
_pb = enc.GetBytes("012345678901234567890a");
var pbN = enc.GetBytes("9012");
Assert.Equal(9, MemUtil.IndexOf(_pb, pbN));
pbN = enc.GetBytes("01234567890123");
Assert.Equal(0, MemUtil.IndexOf(_pb, pbN));
pbN = enc.GetBytes("a");
Assert.Equal(21, MemUtil.IndexOf(_pb, pbN));
pbN = enc.GetBytes("0a");
Assert.Equal(20, MemUtil.IndexOf(_pb, pbN));
pbN = enc.GetBytes("1");
Assert.Equal(1, MemUtil.IndexOf(_pb, pbN));
pbN = enc.GetBytes("b");
Assert.True(MemUtil.IndexOf(_pb, pbN) < 0);
pbN = enc.GetBytes("012b");
Assert.True(MemUtil.IndexOf(_pb, pbN) < 0);
}
[Fact]
public void TestBase32()
{
var pbRes = MemUtil.ParseBase32("MY======");
var pbExp = Encoding.UTF8.GetBytes("f");
Assert.True(MemUtil.ArraysEqual(pbRes, pbExp));
pbRes = MemUtil.ParseBase32("MZXQ====");
pbExp = Encoding.UTF8.GetBytes("fo");
Assert.True(MemUtil.ArraysEqual(pbRes, pbExp));
pbRes = MemUtil.ParseBase32("MZXW6===");
pbExp = Encoding.UTF8.GetBytes("foo");
Assert.True(MemUtil.ArraysEqual(pbRes, pbExp));
pbRes = MemUtil.ParseBase32("MZXW6YQ=");
pbExp = Encoding.UTF8.GetBytes("foob");
Assert.True(MemUtil.ArraysEqual(pbRes, pbExp));
pbRes = MemUtil.ParseBase32("MZXW6YTB");
pbExp = Encoding.UTF8.GetBytes("fooba");
Assert.True(MemUtil.ArraysEqual(pbRes, pbExp));
pbRes = MemUtil.ParseBase32("MZXW6YTBOI======");
pbExp = Encoding.UTF8.GetBytes("foobar");
Assert.True(MemUtil.ArraysEqual(pbRes, pbExp));
pbRes = MemUtil.ParseBase32("JNSXSIDQOJXXM2LEMVZCAYTBONSWIIDPNYQG63TFFV2GS3LFEBYGC43TO5XXEZDTFY======");
pbExp = Encoding.UTF8.GetBytes("Key provider based on one-time passwords.");
Assert.True(MemUtil.ArraysEqual(pbRes, pbExp));
}
[Fact]
public void TestMemUtil2()
{
var i = 0 - 0x10203040;
var pbRes = MemUtil.Int32ToBytes(i);
Assert.Equal("C0CFDFEF", MemUtil.ByteArrayToHexString(pbRes));
Assert.Equal(MemUtil.BytesToUInt32(pbRes), (uint)i);
Assert.Equal(MemUtil.BytesToInt32(pbRes), i);
}
}
}

View File

@@ -0,0 +1,244 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2019 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
using System;
using System.Collections.Generic;
using System.Diagnostics;
using ModernKeePassLib.Interfaces;
namespace ModernKeePassLib.Collections
{
[Flags]
public enum AutoTypeObfuscationOptions
{
None = 0,
UseClipboard = 1
}
public sealed class AutoTypeAssociation : IEquatable<AutoTypeAssociation>,
IDeepCloneable<AutoTypeAssociation>
{
private string m_strWindow = string.Empty;
public string WindowName
{
get { return m_strWindow; }
set
{
Debug.Assert(value != null); if(value == null) throw new ArgumentNullException("value");
m_strWindow = value;
}
}
private string m_strSequence = string.Empty;
public string Sequence
{
get { return m_strSequence; }
set
{
Debug.Assert(value != null); if(value == null) throw new ArgumentNullException("value");
m_strSequence = value;
}
}
public AutoTypeAssociation() { }
public AutoTypeAssociation(string strWindow, string strSeq)
{
if(strWindow == null) throw new ArgumentNullException("strWindow");
if(strSeq == null) throw new ArgumentNullException("strSeq");
m_strWindow = strWindow;
m_strSequence = strSeq;
}
public bool Equals(AutoTypeAssociation other)
{
if(other == null) return false;
if(m_strWindow != other.m_strWindow) return false;
if(m_strSequence != other.m_strSequence) return false;
return true;
}
public AutoTypeAssociation CloneDeep()
{
return (AutoTypeAssociation)this.MemberwiseClone();
}
}
/// <summary>
/// A list of auto-type associations.
/// </summary>
public sealed class AutoTypeConfig : IEquatable<AutoTypeConfig>,
IDeepCloneable<AutoTypeConfig>
{
private bool m_bEnabled = true;
private AutoTypeObfuscationOptions m_atooObfuscation =
AutoTypeObfuscationOptions.None;
private string m_strDefaultSequence = string.Empty;
private List<AutoTypeAssociation> m_lWindowAssocs =
new List<AutoTypeAssociation>();
/// <summary>
/// Specify whether auto-type is enabled or not.
/// </summary>
public bool Enabled
{
get { return m_bEnabled; }
set { m_bEnabled = value; }
}
/// <summary>
/// Specify whether the typing should be obfuscated.
/// </summary>
public AutoTypeObfuscationOptions ObfuscationOptions
{
get { return m_atooObfuscation; }
set { m_atooObfuscation = value; }
}
/// <summary>
/// The default keystroke sequence that is auto-typed if
/// no matching window is found in the <c>Associations</c>
/// container.
/// </summary>
public string DefaultSequence
{
get { return m_strDefaultSequence; }
set
{
Debug.Assert(value != null); if(value == null) throw new ArgumentNullException("value");
m_strDefaultSequence = value;
}
}
/// <summary>
/// Get all auto-type window/keystroke sequence pairs.
/// </summary>
public IEnumerable<AutoTypeAssociation> Associations
{
get { return m_lWindowAssocs; }
}
public int AssociationsCount
{
get { return m_lWindowAssocs.Count; }
}
/// <summary>
/// Construct a new auto-type associations list.
/// </summary>
public AutoTypeConfig()
{
}
/// <summary>
/// Remove all associations.
/// </summary>
public void Clear()
{
m_lWindowAssocs.Clear();
}
/// <summary>
/// Clone the auto-type associations list.
/// </summary>
/// <returns>New, cloned object.</returns>
public AutoTypeConfig CloneDeep()
{
AutoTypeConfig newCfg = new AutoTypeConfig();
newCfg.m_bEnabled = m_bEnabled;
newCfg.m_atooObfuscation = m_atooObfuscation;
newCfg.m_strDefaultSequence = m_strDefaultSequence;
foreach(AutoTypeAssociation a in m_lWindowAssocs)
newCfg.Add(a.CloneDeep());
return newCfg;
}
public bool Equals(AutoTypeConfig other)
{
if(other == null) { Debug.Assert(false); return false; }
if(m_bEnabled != other.m_bEnabled) return false;
if(m_atooObfuscation != other.m_atooObfuscation) return false;
if(m_strDefaultSequence != other.m_strDefaultSequence) return false;
if(m_lWindowAssocs.Count != other.m_lWindowAssocs.Count) return false;
for(int i = 0; i < m_lWindowAssocs.Count; ++i)
{
if(!m_lWindowAssocs[i].Equals(other.m_lWindowAssocs[i]))
return false;
}
return true;
}
public AutoTypeAssociation GetAt(int iIndex)
{
if((iIndex < 0) || (iIndex >= m_lWindowAssocs.Count))
throw new ArgumentOutOfRangeException("iIndex");
return m_lWindowAssocs[iIndex];
}
public void Add(AutoTypeAssociation a)
{
if(a == null) { Debug.Assert(false); throw new ArgumentNullException("a"); }
m_lWindowAssocs.Add(a);
}
public void Insert(int iIndex, AutoTypeAssociation a)
{
if((iIndex < 0) || (iIndex > m_lWindowAssocs.Count))
throw new ArgumentOutOfRangeException("iIndex");
if(a == null) { Debug.Assert(false); throw new ArgumentNullException("a"); }
m_lWindowAssocs.Insert(iIndex, a);
}
public void RemoveAt(int iIndex)
{
if((iIndex < 0) || (iIndex >= m_lWindowAssocs.Count))
throw new ArgumentOutOfRangeException("iIndex");
m_lWindowAssocs.RemoveAt(iIndex);
}
// public void Sort()
// {
// m_lWindowAssocs.Sort(AutoTypeConfig.AssocCompareFn);
// }
// private static int AssocCompareFn(AutoTypeAssociation x,
// AutoTypeAssociation y)
// {
// if(x == null) { Debug.Assert(false); return ((y == null) ? 0 : -1); }
// if(y == null) { Debug.Assert(false); return 1; }
// int cn = x.WindowName.CompareTo(y.WindowName);
// if(cn != 0) return cn;
// return x.Sequence.CompareTo(y.Sequence);
// }
}
}

View File

@@ -0,0 +1,172 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2019 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using ModernKeePassLib.Interfaces;
using ModernKeePassLib.Security;
#if KeePassLibSD
using KeePassLibSD;
#endif
namespace ModernKeePassLib.Collections
{
/// <summary>
/// A list of <c>ProtectedBinary</c> objects (dictionary).
/// </summary>
public sealed class ProtectedBinaryDictionary :
IDeepCloneable<ProtectedBinaryDictionary>,
IEnumerable<KeyValuePair<string, ProtectedBinary>>
{
private SortedDictionary<string, ProtectedBinary> m_vBinaries =
new SortedDictionary<string, ProtectedBinary>();
/// <summary>
/// Get the number of binaries in this entry.
/// </summary>
public uint UCount
{
get { return (uint)m_vBinaries.Count; }
}
/// <summary>
/// Construct a new list of protected binaries.
/// </summary>
public ProtectedBinaryDictionary()
{
}
IEnumerator IEnumerable.GetEnumerator()
{
return m_vBinaries.GetEnumerator();
}
public IEnumerator<KeyValuePair<string, ProtectedBinary>> GetEnumerator()
{
return m_vBinaries.GetEnumerator();
}
public void Clear()
{
m_vBinaries.Clear();
}
/// <summary>
/// Clone the current <c>ProtectedBinaryList</c> object, including all
/// stored protected strings.
/// </summary>
/// <returns>New <c>ProtectedBinaryList</c> object.</returns>
public ProtectedBinaryDictionary CloneDeep()
{
ProtectedBinaryDictionary plNew = new ProtectedBinaryDictionary();
foreach(KeyValuePair<string, ProtectedBinary> kvpBin in m_vBinaries)
{
// ProtectedBinary objects are immutable
plNew.Set(kvpBin.Key, kvpBin.Value);
}
return plNew;
}
public bool EqualsDictionary(ProtectedBinaryDictionary dict)
{
if(dict == null) { Debug.Assert(false); return false; }
if(m_vBinaries.Count != dict.m_vBinaries.Count) return false;
foreach(KeyValuePair<string, ProtectedBinary> kvp in m_vBinaries)
{
ProtectedBinary pb = dict.Get(kvp.Key);
if(pb == null) return false;
if(!pb.Equals(kvp.Value)) return false;
}
return true;
}
/// <summary>
/// Get one of the stored binaries.
/// </summary>
/// <param name="strName">Binary identifier.</param>
/// <returns>Protected binary. If the binary identified by
/// <paramref name="strName" /> cannot be found, the function
/// returns <c>null</c>.</returns>
/// <exception cref="System.ArgumentNullException">Thrown if the input
/// parameter is <c>null</c>.</exception>
public ProtectedBinary Get(string strName)
{
Debug.Assert(strName != null); if(strName == null) throw new ArgumentNullException("strName");
ProtectedBinary pb;
if(m_vBinaries.TryGetValue(strName, out pb)) return pb;
return null;
}
/// <summary>
/// Set a binary object.
/// </summary>
/// <param name="strField">Identifier of the binary field to modify.</param>
/// <param name="pbNewValue">New value. This parameter must not be <c>null</c>.</param>
/// <exception cref="System.ArgumentNullException">Thrown if any of the input
/// parameters is <c>null</c>.</exception>
public void Set(string strField, ProtectedBinary pbNewValue)
{
Debug.Assert(strField != null); if(strField == null) throw new ArgumentNullException("strField");
Debug.Assert(pbNewValue != null); if(pbNewValue == null) throw new ArgumentNullException("pbNewValue");
m_vBinaries[strField] = pbNewValue;
}
/// <summary>
/// Remove a binary object.
/// </summary>
/// <param name="strField">Identifier of the binary field to remove.</param>
/// <returns>Returns <c>true</c> if the object has been successfully
/// removed, otherwise <c>false</c>.</returns>
/// <exception cref="System.ArgumentNullException">Thrown if the input parameter
/// is <c>null</c>.</exception>
public bool Remove(string strField)
{
Debug.Assert(strField != null); if(strField == null) throw new ArgumentNullException("strField");
return m_vBinaries.Remove(strField);
}
public string KeysToString()
{
if(m_vBinaries.Count == 0) return string.Empty;
StringBuilder sb = new StringBuilder();
foreach(KeyValuePair<string, ProtectedBinary> kvp in m_vBinaries)
{
if(sb.Length > 0) sb.Append(", ");
sb.Append(kvp.Key);
}
return sb.ToString();
}
}
}

View File

@@ -0,0 +1,174 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2019 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using ModernKeePassLib.Delegates;
using ModernKeePassLib.Security;
namespace ModernKeePassLib.Collections
{
internal sealed class ProtectedBinarySet : IEnumerable<KeyValuePair<int, ProtectedBinary>>
{
private Dictionary<int, ProtectedBinary> m_d =
new Dictionary<int, ProtectedBinary>();
public ProtectedBinarySet()
{
}
IEnumerator IEnumerable.GetEnumerator()
{
return m_d.GetEnumerator();
}
public IEnumerator<KeyValuePair<int, ProtectedBinary>> GetEnumerator()
{
return m_d.GetEnumerator();
}
public void Clear()
{
m_d.Clear();
}
private int GetFreeID()
{
int i = m_d.Count;
while(m_d.ContainsKey(i)) { ++i; }
Debug.Assert(i == m_d.Count); // m_d.Count should be free
return i;
}
public ProtectedBinary Get(int iID)
{
ProtectedBinary pb;
if(m_d.TryGetValue(iID, out pb)) return pb;
// Debug.Assert(false); // No assert
return null;
}
public int Find(ProtectedBinary pb)
{
if(pb == null) { Debug.Assert(false); return -1; }
// Fast search by reference
foreach(KeyValuePair<int, ProtectedBinary> kvp in m_d)
{
if(object.ReferenceEquals(pb, kvp.Value))
{
Debug.Assert(pb.Equals(kvp.Value));
return kvp.Key;
}
}
// Slow search by content
foreach(KeyValuePair<int, ProtectedBinary> kvp in m_d)
{
if(pb.Equals(kvp.Value)) return kvp.Key;
}
// Debug.Assert(false); // No assert
return -1;
}
public void Set(int iID, ProtectedBinary pb)
{
if(iID < 0) { Debug.Assert(false); return; }
if(pb == null) { Debug.Assert(false); return; }
m_d[iID] = pb;
}
public void Add(ProtectedBinary pb)
{
if(pb == null) { Debug.Assert(false); return; }
int i = Find(pb);
if(i >= 0) return; // Exists already
i = GetFreeID();
m_d[i] = pb;
}
public void AddFrom(ProtectedBinaryDictionary d)
{
if(d == null) { Debug.Assert(false); return; }
foreach(KeyValuePair<string, ProtectedBinary> kvp in d)
{
Add(kvp.Value);
}
}
public void AddFrom(PwGroup pg)
{
if(pg == null) { Debug.Assert(false); return; }
EntryHandler eh = delegate(PwEntry pe)
{
if(pe == null) { Debug.Assert(false); return true; }
AddFrom(pe.Binaries);
foreach(PwEntry peHistory in pe.History)
{
if(peHistory == null) { Debug.Assert(false); continue; }
AddFrom(peHistory.Binaries);
}
return true;
};
pg.TraverseTree(TraversalMethod.PreOrder, null, eh);
}
public ProtectedBinary[] ToArray()
{
int n = m_d.Count;
ProtectedBinary[] v = new ProtectedBinary[n];
foreach(KeyValuePair<int, ProtectedBinary> kvp in m_d)
{
if((kvp.Key < 0) || (kvp.Key >= n))
{
Debug.Assert(false);
throw new InvalidOperationException();
}
v[kvp.Key] = kvp.Value;
}
for(int i = 0; i < n; ++i)
{
if(v[i] == null)
{
Debug.Assert(false);
throw new InvalidOperationException();
}
}
return v;
}
}
}

View File

@@ -0,0 +1,298 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2019 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using ModernKeePassLib.Interfaces;
using ModernKeePassLib.Security;
using ModernKeePassLib.Utility;
#if KeePassLibSD
using KeePassLibSD;
#endif
namespace ModernKeePassLib.Collections
{
/// <summary>
/// A list of <c>ProtectedString</c> objects (dictionary).
/// </summary>
public sealed class ProtectedStringDictionary :
IDeepCloneable<ProtectedStringDictionary>,
IEnumerable<KeyValuePair<string, ProtectedString>>
{
private SortedDictionary<string, ProtectedString> m_vStrings =
new SortedDictionary<string, ProtectedString>();
/// <summary>
/// Get the number of strings in this entry.
/// </summary>
public uint UCount
{
get { return (uint)m_vStrings.Count; }
}
/// <summary>
/// Construct a new list of protected strings.
/// </summary>
public ProtectedStringDictionary()
{
}
IEnumerator IEnumerable.GetEnumerator()
{
return m_vStrings.GetEnumerator();
}
public IEnumerator<KeyValuePair<string, ProtectedString>> GetEnumerator()
{
return m_vStrings.GetEnumerator();
}
public void Clear()
{
m_vStrings.Clear();
}
/// <summary>
/// Clone the current <c>ProtectedStringList</c> object, including all
/// stored protected strings.
/// </summary>
/// <returns>New <c>ProtectedStringList</c> object.</returns>
public ProtectedStringDictionary CloneDeep()
{
ProtectedStringDictionary plNew = new ProtectedStringDictionary();
foreach(KeyValuePair<string, ProtectedString> kvpStr in m_vStrings)
{
// ProtectedString objects are immutable
plNew.Set(kvpStr.Key, kvpStr.Value);
}
return plNew;
}
[Obsolete]
public bool EqualsDictionary(ProtectedStringDictionary dict)
{
return EqualsDictionary(dict, PwCompareOptions.None, MemProtCmpMode.None);
}
[Obsolete]
public bool EqualsDictionary(ProtectedStringDictionary dict,
MemProtCmpMode mpCompare)
{
return EqualsDictionary(dict, PwCompareOptions.None, mpCompare);
}
public bool EqualsDictionary(ProtectedStringDictionary dict,
PwCompareOptions pwOpt, MemProtCmpMode mpCompare)
{
if(dict == null) { Debug.Assert(false); return false; }
bool bNeEqStd = ((pwOpt & PwCompareOptions.NullEmptyEquivStd) !=
PwCompareOptions.None);
if(!bNeEqStd)
{
if(m_vStrings.Count != dict.m_vStrings.Count) return false;
}
foreach(KeyValuePair<string, ProtectedString> kvp in m_vStrings)
{
bool bStdField = PwDefs.IsStandardField(kvp.Key);
ProtectedString ps = dict.Get(kvp.Key);
if(bNeEqStd && (ps == null) && bStdField)
ps = ProtectedString.Empty;
if(ps == null) return false;
if(mpCompare == MemProtCmpMode.Full)
{
if(ps.IsProtected != kvp.Value.IsProtected) return false;
}
else if(mpCompare == MemProtCmpMode.CustomOnly)
{
if(!bStdField && (ps.IsProtected != kvp.Value.IsProtected))
return false;
}
if(!ps.Equals(kvp.Value, false)) return false;
}
if(bNeEqStd)
{
foreach(KeyValuePair<string, ProtectedString> kvp in dict.m_vStrings)
{
ProtectedString ps = Get(kvp.Key);
if(ps != null) continue; // Compared previously
if(!PwDefs.IsStandardField(kvp.Key)) return false;
if(!kvp.Value.IsEmpty) return false;
}
}
return true;
}
/// <summary>
/// Get one of the protected strings.
/// </summary>
/// <param name="strName">String identifier.</param>
/// <returns>Protected string. If the string identified by
/// <paramref name="strName" /> cannot be found, the function
/// returns <c>null</c>.</returns>
/// <exception cref="System.ArgumentNullException">Thrown if the input parameter
/// is <c>null</c>.</exception>
public ProtectedString Get(string strName)
{
Debug.Assert(strName != null); if(strName == null) throw new ArgumentNullException("strName");
ProtectedString ps;
if(m_vStrings.TryGetValue(strName, out ps)) return ps;
return null;
}
/// <summary>
/// Get one of the protected strings. The return value is never <c>null</c>.
/// If the requested string cannot be found, an empty protected string
/// object is returned.
/// </summary>
/// <param name="strName">String identifier.</param>
/// <returns>Returns a protected string object. If the standard string
/// has not been set yet, the return value is an empty string (<c>""</c>).</returns>
/// <exception cref="System.ArgumentNullException">Thrown if the input
/// parameter is <c>null</c>.</exception>
public ProtectedString GetSafe(string strName)
{
Debug.Assert(strName != null); if(strName == null) throw new ArgumentNullException("strName");
ProtectedString ps;
if(m_vStrings.TryGetValue(strName, out ps)) return ps;
return ProtectedString.Empty;
}
/// <summary>
/// Test if a named string exists.
/// </summary>
/// <param name="strName">Name of the string to try.</param>
/// <returns>Returns <c>true</c> if the string exists, otherwise <c>false</c>.</returns>
/// <exception cref="System.ArgumentNullException">Thrown if
/// <paramref name="strName" /> is <c>null</c>.</exception>
public bool Exists(string strName)
{
Debug.Assert(strName != null); if(strName == null) throw new ArgumentNullException("strName");
return m_vStrings.ContainsKey(strName);
}
/// <summary>
/// Get one of the protected strings. If the string doesn't exist, the
/// return value is an empty string (<c>""</c>).
/// </summary>
/// <param name="strName">Name of the requested string.</param>
/// <returns>Requested string value or an empty string, if the named
/// string doesn't exist.</returns>
/// <exception cref="System.ArgumentNullException">Thrown if the input
/// parameter is <c>null</c>.</exception>
public string ReadSafe(string strName)
{
Debug.Assert(strName != null); if(strName == null) throw new ArgumentNullException("strName");
ProtectedString ps;
if(m_vStrings.TryGetValue(strName, out ps))
return ps.ReadString();
return string.Empty;
}
/// <summary>
/// Get one of the entry strings. If the string doesn't exist, the
/// return value is an empty string (<c>""</c>). If the string is
/// in-memory protected, the return value is <c>PwDefs.HiddenPassword</c>.
/// </summary>
/// <param name="strName">Name of the requested string.</param>
/// <returns>Returns the requested string in plain-text or
/// <c>PwDefs.HiddenPassword</c> if the string cannot be found.</returns>
/// <exception cref="System.ArgumentNullException">Thrown if the input
/// parameter is <c>null</c>.</exception>
public string ReadSafeEx(string strName)
{
Debug.Assert(strName != null); if(strName == null) throw new ArgumentNullException("strName");
ProtectedString ps;
if(m_vStrings.TryGetValue(strName, out ps))
{
if(ps.IsProtected) return PwDefs.HiddenPassword;
return ps.ReadString();
}
return string.Empty;
}
/// <summary>
/// Set a string.
/// </summary>
/// <param name="strField">Identifier of the string field to modify.</param>
/// <param name="psNewValue">New value. This parameter must not be <c>null</c>.</param>
/// <exception cref="System.ArgumentNullException">Thrown if one of the input
/// parameters is <c>null</c>.</exception>
public void Set(string strField, ProtectedString psNewValue)
{
Debug.Assert(strField != null); if(strField == null) throw new ArgumentNullException("strField");
Debug.Assert(psNewValue != null); if(psNewValue == null) throw new ArgumentNullException("psNewValue");
m_vStrings[strField] = psNewValue;
}
/// <summary>
/// Delete a string.
/// </summary>
/// <param name="strField">Name of the string field to delete.</param>
/// <returns>Returns <c>true</c> if the field has been successfully
/// removed, otherwise the return value is <c>false</c>.</returns>
/// <exception cref="System.ArgumentNullException">Thrown if the input
/// parameter is <c>null</c>.</exception>
public bool Remove(string strField)
{
Debug.Assert(strField != null); if(strField == null) throw new ArgumentNullException("strField");
return m_vStrings.Remove(strField);
}
public List<string> GetKeys()
{
return new List<string>(m_vStrings.Keys);
}
public void EnableProtection(string strField, bool bProtect)
{
ProtectedString ps = Get(strField);
if(ps == null) return; // Nothing to do, no assert
if(ps.IsProtected != bProtect)
Set(strField, ps.WithProtection(bProtect));
}
}
}

View File

@@ -0,0 +1,380 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2019 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using ModernKeePassLib.Interfaces;
namespace ModernKeePassLib.Collections
{
/// <summary>
/// List of objects that implement <c>IDeepCloneable</c>,
/// and cannot be <c>null</c>.
/// </summary>
/// <typeparam name="T">Type specifier.</typeparam>
public sealed class PwObjectList<T> : IEnumerable<T>
where T : class, IDeepCloneable<T>
{
private List<T> m_vObjects = new List<T>();
/// <summary>
/// Get number of objects in this list.
/// </summary>
public uint UCount
{
get { return (uint)m_vObjects.Count; }
}
/// <summary>
/// Construct a new list of objects.
/// </summary>
public PwObjectList()
{
}
IEnumerator IEnumerable.GetEnumerator()
{
return m_vObjects.GetEnumerator();
}
public IEnumerator<T> GetEnumerator()
{
return m_vObjects.GetEnumerator();
}
public void Clear()
{
// Do not destroy contained objects!
m_vObjects.Clear();
}
/// <summary>
/// Clone the current <c>PwObjectList</c>, including all
/// stored objects (deep copy).
/// </summary>
/// <returns>New <c>PwObjectList</c>.</returns>
public PwObjectList<T> CloneDeep()
{
PwObjectList<T> pl = new PwObjectList<T>();
foreach(T po in m_vObjects)
pl.Add(po.CloneDeep());
return pl;
}
public PwObjectList<T> CloneShallow()
{
PwObjectList<T> tNew = new PwObjectList<T>();
foreach(T po in m_vObjects) tNew.Add(po);
return tNew;
}
public List<T> CloneShallowToList()
{
PwObjectList<T> tNew = CloneShallow();
return tNew.m_vObjects;
}
/// <summary>
/// Add an object to this list.
/// </summary>
/// <param name="pwObject">Object to be added.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the input
/// parameter is <c>null</c>.</exception>
public void Add(T pwObject)
{
Debug.Assert(pwObject != null);
if(pwObject == null) throw new ArgumentNullException("pwObject");
m_vObjects.Add(pwObject);
}
public void Add(PwObjectList<T> vObjects)
{
Debug.Assert(vObjects != null);
if(vObjects == null) throw new ArgumentNullException("vObjects");
foreach(T po in vObjects)
{
m_vObjects.Add(po);
}
}
public void Add(List<T> vObjects)
{
Debug.Assert(vObjects != null);
if(vObjects == null) throw new ArgumentNullException("vObjects");
foreach(T po in vObjects)
{
m_vObjects.Add(po);
}
}
public void Insert(uint uIndex, T pwObject)
{
Debug.Assert(pwObject != null);
if(pwObject == null) throw new ArgumentNullException("pwObject");
m_vObjects.Insert((int)uIndex, pwObject);
}
/// <summary>
/// Get an object of the list.
/// </summary>
/// <param name="uIndex">Index of the object to get. Must be valid, otherwise an
/// exception is thrown.</param>
/// <returns>Reference to an existing <c>T</c> object. Is never <c>null</c>.</returns>
public T GetAt(uint uIndex)
{
Debug.Assert(uIndex < m_vObjects.Count);
if(uIndex >= m_vObjects.Count) throw new ArgumentOutOfRangeException("uIndex");
return m_vObjects[(int)uIndex];
}
public void SetAt(uint uIndex, T pwObject)
{
Debug.Assert(pwObject != null);
if(pwObject == null) throw new ArgumentNullException("pwObject");
if(uIndex >= (uint)m_vObjects.Count)
throw new ArgumentOutOfRangeException("uIndex");
m_vObjects[(int)uIndex] = pwObject;
}
/// <summary>
/// Get a range of objects.
/// </summary>
/// <param name="uStartIndexIncl">Index of the first object to be
/// returned (inclusive).</param>
/// <param name="uEndIndexIncl">Index of the last object to be
/// returned (inclusive).</param>
/// <returns></returns>
public List<T> GetRange(uint uStartIndexIncl, uint uEndIndexIncl)
{
if(uStartIndexIncl >= (uint)m_vObjects.Count)
throw new ArgumentOutOfRangeException("uStartIndexIncl");
if(uEndIndexIncl >= (uint)m_vObjects.Count)
throw new ArgumentOutOfRangeException("uEndIndexIncl");
if(uStartIndexIncl > uEndIndexIncl)
throw new ArgumentException();
List<T> list = new List<T>((int)(uEndIndexIncl - uStartIndexIncl) + 1);
for(uint u = uStartIndexIncl; u <= uEndIndexIncl; ++u)
{
list.Add(m_vObjects[(int)u]);
}
return list;
}
public int IndexOf(T pwReference)
{
Debug.Assert(pwReference != null); if(pwReference == null) throw new ArgumentNullException("pwReference");
return m_vObjects.IndexOf(pwReference);
}
/// <summary>
/// Delete an object of this list. The object to be deleted is identified
/// by a reference handle.
/// </summary>
/// <param name="pwReference">Reference of the object to be deleted.</param>
/// <returns>Returns <c>true</c> if the object was deleted, <c>false</c> if
/// the object wasn't found in this list.</returns>
/// <exception cref="System.ArgumentNullException">Thrown if the input
/// parameter is <c>null</c>.</exception>
public bool Remove(T pwReference)
{
Debug.Assert(pwReference != null); if(pwReference == null) throw new ArgumentNullException("pwReference");
return m_vObjects.Remove(pwReference);
}
public void RemoveAt(uint uIndex)
{
m_vObjects.RemoveAt((int)uIndex);
}
/// <summary>
/// Move an object up or down.
/// </summary>
/// <param name="tObject">The object to be moved.</param>
/// <param name="bUp">Move one up. If <c>false</c>, move one down.</param>
public void MoveOne(T tObject, bool bUp)
{
Debug.Assert(tObject != null);
if(tObject == null) throw new ArgumentNullException("tObject");
int nCount = m_vObjects.Count;
if(nCount <= 1) return;
int nIndex = m_vObjects.IndexOf(tObject);
if(nIndex < 0) { Debug.Assert(false); return; }
if(bUp && (nIndex > 0)) // No assert for top item
{
T tTemp = m_vObjects[nIndex - 1];
m_vObjects[nIndex - 1] = m_vObjects[nIndex];
m_vObjects[nIndex] = tTemp;
}
else if(!bUp && (nIndex != (nCount - 1))) // No assert for bottom item
{
T tTemp = m_vObjects[nIndex + 1];
m_vObjects[nIndex + 1] = m_vObjects[nIndex];
m_vObjects[nIndex] = tTemp;
}
}
public void MoveOne(T[] vObjects, bool bUp)
{
Debug.Assert(vObjects != null);
if(vObjects == null) throw new ArgumentNullException("vObjects");
List<int> lIndices = new List<int>();
foreach(T t in vObjects)
{
if(t == null) { Debug.Assert(false); continue; }
int p = IndexOf(t);
if(p >= 0) lIndices.Add(p);
else { Debug.Assert(false); }
}
MoveOne(lIndices.ToArray(), bUp);
}
public void MoveOne(int[] vIndices, bool bUp)
{
Debug.Assert(vIndices != null);
if(vIndices == null) throw new ArgumentNullException("vIndices");
int n = m_vObjects.Count;
if(n <= 1) return; // No moving possible
int m = vIndices.Length;
if(m == 0) return; // Nothing to move
int[] v = new int[m];
Array.Copy(vIndices, v, m);
Array.Sort<int>(v);
if((bUp && (v[0] <= 0)) || (!bUp && (v[m - 1] >= (n - 1))))
return; // Moving as a block is not possible
int iStart = (bUp ? 0 : (m - 1));
int iExcl = (bUp ? m : -1);
int iStep = (bUp ? 1 : -1);
for(int i = iStart; i != iExcl; i += iStep)
{
int p = v[i];
if((p < 0) || (p >= n)) { Debug.Assert(false); continue; }
T t = m_vObjects[p];
if(bUp)
{
Debug.Assert(p > 0);
m_vObjects.RemoveAt(p);
m_vObjects.Insert(p - 1, t);
}
else // Down
{
Debug.Assert(p < (n - 1));
m_vObjects.RemoveAt(p);
m_vObjects.Insert(p + 1, t);
}
}
}
/// <summary>
/// Move some of the objects in this list to the top/bottom.
/// </summary>
/// <param name="vObjects">List of objects to be moved.</param>
/// <param name="bTop">Move to top. If <c>false</c>, move to bottom.</param>
public void MoveTopBottom(T[] vObjects, bool bTop)
{
Debug.Assert(vObjects != null);
if(vObjects == null) throw new ArgumentNullException("vObjects");
if(vObjects.Length == 0) return;
int nCount = m_vObjects.Count;
foreach(T t in vObjects) m_vObjects.Remove(t);
if(bTop)
{
int nPos = 0;
foreach(T t in vObjects)
{
m_vObjects.Insert(nPos, t);
++nPos;
}
}
else // Move to bottom
{
foreach(T t in vObjects) m_vObjects.Add(t);
}
Debug.Assert(nCount == m_vObjects.Count);
if(nCount != m_vObjects.Count)
throw new ArgumentException("At least one of the T objects in the vObjects list doesn't exist!");
}
public void Sort(IComparer<T> tComparer)
{
if(tComparer == null) throw new ArgumentNullException("tComparer");
m_vObjects.Sort(tComparer);
}
public void Sort(Comparison<T> tComparison)
{
if(tComparison == null) throw new ArgumentNullException("tComparison");
m_vObjects.Sort(tComparison);
}
public static PwObjectList<T> FromArray(T[] tArray)
{
if(tArray == null) throw new ArgumentNullException("tArray");
PwObjectList<T> l = new PwObjectList<T>();
foreach(T t in tArray) { l.Add(t); }
return l;
}
public static PwObjectList<T> FromList(List<T> tList)
{
if(tList == null) throw new ArgumentNullException("tList");
PwObjectList<T> l = new PwObjectList<T>();
l.Add(tList);
return l;
}
}
}

View File

@@ -0,0 +1,232 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2019 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using ModernKeePassLib.Delegates;
using ModernKeePassLib.Interfaces;
using ModernKeePassLib.Utility;
#if KeePassLibSD
using KeePassLibSD;
#endif
namespace ModernKeePassLib.Collections
{
public sealed class PwObjectPool
{
private SortedDictionary<PwUuid, IStructureItem> m_dict =
new SortedDictionary<PwUuid, IStructureItem>();
public static PwObjectPool FromGroupRecursive(PwGroup pgRoot, bool bEntries)
{
if(pgRoot == null) throw new ArgumentNullException("pgRoot");
PwObjectPool p = new PwObjectPool();
if(!bEntries) p.m_dict[pgRoot.Uuid] = pgRoot;
GroupHandler gh = delegate(PwGroup pg)
{
p.m_dict[pg.Uuid] = pg;
return true;
};
EntryHandler eh = delegate(PwEntry pe)
{
p.m_dict[pe.Uuid] = pe;
return true;
};
pgRoot.TraverseTree(TraversalMethod.PreOrder, bEntries ? null : gh,
bEntries ? eh : null);
return p;
}
public IStructureItem Get(PwUuid pwUuid)
{
IStructureItem pItem;
m_dict.TryGetValue(pwUuid, out pItem);
return pItem;
}
public bool ContainsOnlyType(Type t)
{
foreach(KeyValuePair<PwUuid, IStructureItem> kvp in m_dict)
{
if(kvp.Value.GetType() != t) return false;
}
return true;
}
}
internal sealed class PwObjectPoolEx
{
private Dictionary<PwUuid, ulong> m_dUuidToId =
new Dictionary<PwUuid, ulong>();
private Dictionary<ulong, IStructureItem> m_dIdToItem =
new Dictionary<ulong, IStructureItem>();
private PwObjectPoolEx()
{
}
public static PwObjectPoolEx FromGroup(PwGroup pg)
{
PwObjectPoolEx p = new PwObjectPoolEx();
if(pg == null) { Debug.Assert(false); return p; }
ulong uFreeId = 2; // 0 = "not found", 1 is a hole
p.m_dUuidToId[pg.Uuid] = uFreeId;
p.m_dIdToItem[uFreeId] = pg;
uFreeId += 2; // Make hole
p.AddGroupRec(pg, ref uFreeId);
return p;
}
private void AddGroupRec(PwGroup pg, ref ulong uFreeId)
{
if(pg == null) { Debug.Assert(false); return; }
ulong uId = uFreeId;
// Consecutive entries must have consecutive IDs
foreach(PwEntry pe in pg.Entries)
{
Debug.Assert(!m_dUuidToId.ContainsKey(pe.Uuid));
Debug.Assert(!m_dIdToItem.ContainsValue(pe));
m_dUuidToId[pe.Uuid] = uId;
m_dIdToItem[uId] = pe;
++uId;
}
++uId; // Make hole
// Consecutive groups must have consecutive IDs
foreach(PwGroup pgSub in pg.Groups)
{
Debug.Assert(!m_dUuidToId.ContainsKey(pgSub.Uuid));
Debug.Assert(!m_dIdToItem.ContainsValue(pgSub));
m_dUuidToId[pgSub.Uuid] = uId;
m_dIdToItem[uId] = pgSub;
++uId;
}
++uId; // Make hole
foreach(PwGroup pgSub in pg.Groups)
{
AddGroupRec(pgSub, ref uId);
}
uFreeId = uId;
}
public ulong GetIdByUuid(PwUuid pwUuid)
{
if(pwUuid == null) { Debug.Assert(false); return 0; }
ulong uId;
m_dUuidToId.TryGetValue(pwUuid, out uId);
return uId;
}
public IStructureItem GetItemByUuid(PwUuid pwUuid)
{
if(pwUuid == null) { Debug.Assert(false); return null; }
ulong uId;
if(!m_dUuidToId.TryGetValue(pwUuid, out uId)) return null;
Debug.Assert(uId != 0);
return GetItemById(uId);
}
public IStructureItem GetItemById(ulong uId)
{
IStructureItem p;
m_dIdToItem.TryGetValue(uId, out p);
return p;
}
}
internal sealed class PwObjectBlock<T> : IEnumerable<T>
where T : class, ITimeLogger, IStructureItem, IDeepCloneable<T>
{
private List<T> m_l = new List<T>();
public T PrimaryItem
{
get { return ((m_l.Count > 0) ? m_l[0] : null); }
}
private DateTime m_dtLocationChanged = TimeUtil.SafeMinValueUtc;
public DateTime LocationChanged
{
get { return m_dtLocationChanged; }
}
private PwObjectPoolEx m_poolAssoc = null;
public PwObjectPoolEx PoolAssoc
{
get { return m_poolAssoc; }
}
public PwObjectBlock()
{
}
#if DEBUG
public override string ToString()
{
return ("PwObjectBlock, Count = " + m_l.Count.ToString());
}
#endif
IEnumerator IEnumerable.GetEnumerator()
{
return m_l.GetEnumerator();
}
public IEnumerator<T> GetEnumerator()
{
return m_l.GetEnumerator();
}
public void Add(T t, DateTime dtLoc, PwObjectPoolEx pool)
{
if(t == null) { Debug.Assert(false); return; }
m_l.Add(t);
if(dtLoc > m_dtLocationChanged)
{
m_dtLocationChanged = dtLoc;
m_poolAssoc = pool;
}
}
}
}

View File

@@ -0,0 +1,130 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2019 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using ModernKeePassLib.Interfaces;
#if KeePassLibSD
using KeePassLibSD;
#endif
namespace ModernKeePassLib.Collections
{
public sealed class StringDictionaryEx : IDeepCloneable<StringDictionaryEx>,
IEnumerable<KeyValuePair<string, string>>, IEquatable<StringDictionaryEx>
{
private SortedDictionary<string, string> m_dict =
new SortedDictionary<string, string>();
public int Count
{
get { return m_dict.Count; }
}
public StringDictionaryEx()
{
}
IEnumerator IEnumerable.GetEnumerator()
{
return m_dict.GetEnumerator();
}
public IEnumerator<KeyValuePair<string, string>> GetEnumerator()
{
return m_dict.GetEnumerator();
}
public StringDictionaryEx CloneDeep()
{
StringDictionaryEx sdNew = new StringDictionaryEx();
foreach(KeyValuePair<string, string> kvp in m_dict)
sdNew.m_dict[kvp.Key] = kvp.Value; // Strings are immutable
return sdNew;
}
public bool Equals(StringDictionaryEx sdOther)
{
if(sdOther == null) { Debug.Assert(false); return false; }
if(m_dict.Count != sdOther.m_dict.Count) return false;
foreach(KeyValuePair<string, string> kvp in sdOther.m_dict)
{
string str = Get(kvp.Key);
if((str == null) || (str != kvp.Value)) return false;
}
return true;
}
public string Get(string strName)
{
if(strName == null) { Debug.Assert(false); throw new ArgumentNullException("strName"); }
string s;
if(m_dict.TryGetValue(strName, out s)) return s;
return null;
}
public bool Exists(string strName)
{
if(strName == null) { Debug.Assert(false); throw new ArgumentNullException("strName"); }
return m_dict.ContainsKey(strName);
}
/// <summary>
/// Set a string.
/// </summary>
/// <param name="strField">Identifier of the string field to modify.</param>
/// <param name="strNewValue">New value. This parameter must not be <c>null</c>.</param>
/// <exception cref="System.ArgumentNullException">Thrown if one of the input
/// parameters is <c>null</c>.</exception>
public void Set(string strField, string strNewValue)
{
if(strField == null) { Debug.Assert(false); throw new ArgumentNullException("strField"); }
if(strNewValue == null) { Debug.Assert(false); throw new ArgumentNullException("strNewValue"); }
m_dict[strField] = strNewValue;
}
/// <summary>
/// Delete a string.
/// </summary>
/// <param name="strField">Name of the string field to delete.</param>
/// <returns>Returns <c>true</c>, if the field has been successfully
/// removed. Otherwise, the return value is <c>false</c>.</returns>
/// <exception cref="System.ArgumentNullException">Thrown if the input
/// parameter is <c>null</c>.</exception>
public bool Remove(string strField)
{
if(strField == null) { Debug.Assert(false); throw new ArgumentNullException("strField"); }
return m_dict.Remove(strField);
}
}
}

View File

@@ -0,0 +1,415 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2019 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;
using ModernKeePassLib.Resources;
using ModernKeePassLib.Utility;
namespace ModernKeePassLib.Collections
{
public class VariantDictionary
{
private const ushort VdVersion = 0x0100;
private const ushort VdmCritical = 0xFF00;
private const ushort VdmInfo = 0x00FF;
private Dictionary<string, object> m_d = new Dictionary<string, object>();
private enum VdType : byte
{
None = 0,
// Byte = 0x02,
// UInt16 = 0x03,
UInt32 = 0x04,
UInt64 = 0x05,
// Signed mask: 0x08
Bool = 0x08,
// SByte = 0x0A,
// Int16 = 0x0B,
Int32 = 0x0C,
Int64 = 0x0D,
// Float = 0x10,
// Double = 0x11,
// Decimal = 0x12,
// Char = 0x17, // 16-bit Unicode character
String = 0x18,
// Array mask: 0x40
ByteArray = 0x42
}
public int Count
{
get { return m_d.Count; }
}
public VariantDictionary()
{
Debug.Assert((VdmCritical & VdmInfo) == ushort.MinValue);
Debug.Assert((VdmCritical | VdmInfo) == ushort.MaxValue);
}
private bool Get<T>(string strName, out T t)
{
t = default(T);
if(string.IsNullOrEmpty(strName)) { Debug.Assert(false); return false; }
object o;
if(!m_d.TryGetValue(strName, out o)) return false; // No assert
if(o == null) { Debug.Assert(false); return false; }
if(o.GetType() != typeof(T)) { Debug.Assert(false); return false; }
t = (T)o;
return true;
}
private void SetStruct<T>(string strName, T t)
where T : struct
{
if(string.IsNullOrEmpty(strName)) { Debug.Assert(false); return; }
#if DEBUG
T tEx;
Get<T>(strName, out tEx); // Assert same type
#endif
m_d[strName] = t;
}
private void SetRef<T>(string strName, T t)
where T : class
{
if(string.IsNullOrEmpty(strName)) { Debug.Assert(false); return; }
if(t == null) { Debug.Assert(false); return; }
#if DEBUG
T tEx;
Get<T>(strName, out tEx); // Assert same type
#endif
m_d[strName] = t;
}
public bool Remove(string strName)
{
if(string.IsNullOrEmpty(strName)) { Debug.Assert(false); return false; }
return m_d.Remove(strName);
}
public void CopyTo(VariantDictionary d)
{
if(d == null) { Debug.Assert(false); return; }
// Do not clear the target
foreach(KeyValuePair<string, object> kvp in m_d)
{
d.m_d[kvp.Key] = kvp.Value;
}
}
public Type GetTypeOf(string strName)
{
if(string.IsNullOrEmpty(strName)) { Debug.Assert(false); return null; }
object o;
m_d.TryGetValue(strName, out o);
if(o == null) return null; // No assert
return o.GetType();
}
public uint GetUInt32(string strName, uint uDefault)
{
uint u;
if(Get<uint>(strName, out u)) return u;
return uDefault;
}
public void SetUInt32(string strName, uint uValue)
{
SetStruct<uint>(strName, uValue);
}
public ulong GetUInt64(string strName, ulong uDefault)
{
ulong u;
if(Get<ulong>(strName, out u)) return u;
return uDefault;
}
public void SetUInt64(string strName, ulong uValue)
{
SetStruct<ulong>(strName, uValue);
}
public bool GetBool(string strName, bool bDefault)
{
bool b;
if(Get<bool>(strName, out b)) return b;
return bDefault;
}
public void SetBool(string strName, bool bValue)
{
SetStruct<bool>(strName, bValue);
}
public int GetInt32(string strName, int iDefault)
{
int i;
if(Get<int>(strName, out i)) return i;
return iDefault;
}
public void SetInt32(string strName, int iValue)
{
SetStruct<int>(strName, iValue);
}
public long GetInt64(string strName, long lDefault)
{
long l;
if(Get<long>(strName, out l)) return l;
return lDefault;
}
public void SetInt64(string strName, long lValue)
{
SetStruct<long>(strName, lValue);
}
public string GetString(string strName)
{
string str;
Get<string>(strName, out str);
return str;
}
public void SetString(string strName, string strValue)
{
SetRef<string>(strName, strValue);
}
public byte[] GetByteArray(string strName)
{
byte[] pb;
Get<byte[]>(strName, out pb);
return pb;
}
public void SetByteArray(string strName, byte[] pbValue)
{
SetRef<byte[]>(strName, pbValue);
}
/// <summary>
/// Create a deep copy.
/// </summary>
public virtual object Clone()
{
VariantDictionary vdNew = new VariantDictionary();
foreach(KeyValuePair<string, object> kvp in m_d)
{
object o = kvp.Value;
if(o == null) { Debug.Assert(false); continue; }
Type t = o.GetType();
if(t == typeof(byte[]))
{
byte[] p = (byte[])o;
byte[] pNew = new byte[p.Length];
if(p.Length > 0) Array.Copy(p, pNew, p.Length);
o = pNew;
}
vdNew.m_d[kvp.Key] = o;
}
return vdNew;
}
public static byte[] Serialize(VariantDictionary p)
{
if(p == null) { Debug.Assert(false); return null; }
byte[] pbRet;
using(MemoryStream ms = new MemoryStream())
{
MemUtil.Write(ms, MemUtil.UInt16ToBytes(VdVersion));
foreach(KeyValuePair<string, object> kvp in p.m_d)
{
string strName = kvp.Key;
if(string.IsNullOrEmpty(strName)) { Debug.Assert(false); continue; }
byte[] pbName = StrUtil.Utf8.GetBytes(strName);
object o = kvp.Value;
if(o == null) { Debug.Assert(false); continue; }
Type t = o.GetType();
VdType vt = VdType.None;
byte[] pbValue = null;
if(t == typeof(uint))
{
vt = VdType.UInt32;
pbValue = MemUtil.UInt32ToBytes((uint)o);
}
else if(t == typeof(ulong))
{
vt = VdType.UInt64;
pbValue = MemUtil.UInt64ToBytes((ulong)o);
}
else if(t == typeof(bool))
{
vt = VdType.Bool;
pbValue = new byte[1];
pbValue[0] = ((bool)o ? (byte)1 : (byte)0);
}
else if(t == typeof(int))
{
vt = VdType.Int32;
pbValue = MemUtil.Int32ToBytes((int)o);
}
else if(t == typeof(long))
{
vt = VdType.Int64;
pbValue = MemUtil.Int64ToBytes((long)o);
}
else if(t == typeof(string))
{
vt = VdType.String;
pbValue = StrUtil.Utf8.GetBytes((string)o);
}
else if(t == typeof(byte[]))
{
vt = VdType.ByteArray;
pbValue = (byte[])o;
}
else { Debug.Assert(false); continue; } // Unknown type
ms.WriteByte((byte)vt);
MemUtil.Write(ms, MemUtil.Int32ToBytes(pbName.Length));
MemUtil.Write(ms, pbName);
MemUtil.Write(ms, MemUtil.Int32ToBytes(pbValue.Length));
MemUtil.Write(ms, pbValue);
}
ms.WriteByte((byte)VdType.None);
pbRet = ms.ToArray();
}
return pbRet;
}
public static VariantDictionary Deserialize(byte[] pb)
{
if(pb == null) { Debug.Assert(false); return null; }
VariantDictionary d = new VariantDictionary();
using(MemoryStream ms = new MemoryStream(pb, false))
{
ushort uVersion = MemUtil.BytesToUInt16(MemUtil.Read(ms, 2));
if((uVersion & VdmCritical) > (VdVersion & VdmCritical))
throw new FormatException(KLRes.FileNewVerReq);
while(true)
{
int iType = ms.ReadByte();
if(iType < 0) throw new EndOfStreamException(KLRes.FileCorrupted);
byte btType = (byte)iType;
if(btType == (byte)VdType.None) break;
int cbName = MemUtil.BytesToInt32(MemUtil.Read(ms, 4));
byte[] pbName = MemUtil.Read(ms, cbName);
if(pbName.Length != cbName)
throw new EndOfStreamException(KLRes.FileCorrupted);
string strName = StrUtil.Utf8.GetString(pbName, 0, pbName.Length);
int cbValue = MemUtil.BytesToInt32(MemUtil.Read(ms, 4));
byte[] pbValue = MemUtil.Read(ms, cbValue);
if(pbValue.Length != cbValue)
throw new EndOfStreamException(KLRes.FileCorrupted);
switch(btType)
{
case (byte)VdType.UInt32:
if(cbValue == 4)
d.SetUInt32(strName, MemUtil.BytesToUInt32(pbValue));
else { Debug.Assert(false); }
break;
case (byte)VdType.UInt64:
if(cbValue == 8)
d.SetUInt64(strName, MemUtil.BytesToUInt64(pbValue));
else { Debug.Assert(false); }
break;
case (byte)VdType.Bool:
if(cbValue == 1)
d.SetBool(strName, (pbValue[0] != 0));
else { Debug.Assert(false); }
break;
case (byte)VdType.Int32:
if(cbValue == 4)
d.SetInt32(strName, MemUtil.BytesToInt32(pbValue));
else { Debug.Assert(false); }
break;
case (byte)VdType.Int64:
if(cbValue == 8)
d.SetInt64(strName, MemUtil.BytesToInt64(pbValue));
else { Debug.Assert(false); }
break;
case (byte)VdType.String:
d.SetString(strName, StrUtil.Utf8.GetString(pbValue, 0, pbValue.Length));
break;
case (byte)VdType.ByteArray:
d.SetByteArray(strName, pbValue);
break;
default:
Debug.Assert(false); // Unknown type
break;
}
}
Debug.Assert(ms.ReadByte() < 0);
}
return d;
}
}
}

View File

@@ -0,0 +1,254 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2019 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using ModernKeePassLib.Resources;
using ModernKeePassLib.Utility;
namespace ModernKeePassLib.Cryptography.Cipher
{
/// <summary>
/// Implementation of the ChaCha20 cipher with a 96-bit nonce,
/// as specified in RFC 7539.
/// https://tools.ietf.org/html/rfc7539
/// </summary>
public sealed class ChaCha20Cipher : CtrBlockCipher
{
private uint[] m_s = new uint[16]; // State
private uint[] m_x = new uint[16]; // Working buffer
private bool m_bLargeCounter; // See constructor documentation
private static readonly uint[] g_sigma = new uint[4] {
0x61707865, 0x3320646E, 0x79622D32, 0x6B206574
};
private const string StrNameRfc = "ChaCha20 (RFC 7539)";
public override int BlockSize
{
get { return 64; }
}
public ChaCha20Cipher(byte[] pbKey32, byte[] pbIV12) :
this(pbKey32, pbIV12, false)
{
}
/// <summary>
/// Constructor.
/// </summary>
/// <param name="pbKey32">Key (32 bytes).</param>
/// <param name="pbIV12">Nonce (12 bytes).</param>
/// <param name="bLargeCounter">If <c>false</c>, the RFC 7539 version
/// of ChaCha20 is used. In this case, only 256 GB of data can be
/// encrypted securely (because the block counter is a 32-bit variable);
/// an attempt to encrypt more data throws an exception.
/// If <paramref name="bLargeCounter" /> is <c>true</c>, the 32-bit
/// counter overflows to another 32-bit variable (i.e. the counter
/// effectively is a 64-bit variable), like in the original ChaCha20
/// specification by D. J. Bernstein (which has a 64-bit counter and a
/// 64-bit nonce). To be compatible with this version, the 64-bit nonce
/// must be stored in the last 8 bytes of <paramref name="pbIV12" />
/// and the first 4 bytes must be 0.
/// If the IV was generated randomly, a 12-byte IV and a large counter
/// can be used to securely encrypt more than 256 GB of data (but note
/// this is incompatible with RFC 7539 and the original specification).</param>
public ChaCha20Cipher(byte[] pbKey32, byte[] pbIV12, bool bLargeCounter) :
base()
{
if(pbKey32 == null) throw new ArgumentNullException("pbKey32");
if(pbKey32.Length != 32) throw new ArgumentOutOfRangeException("pbKey32");
if(pbIV12 == null) throw new ArgumentNullException("pbIV12");
if(pbIV12.Length != 12) throw new ArgumentOutOfRangeException("pbIV12");
m_bLargeCounter = bLargeCounter;
// Key setup
m_s[4] = MemUtil.BytesToUInt32(pbKey32, 0);
m_s[5] = MemUtil.BytesToUInt32(pbKey32, 4);
m_s[6] = MemUtil.BytesToUInt32(pbKey32, 8);
m_s[7] = MemUtil.BytesToUInt32(pbKey32, 12);
m_s[8] = MemUtil.BytesToUInt32(pbKey32, 16);
m_s[9] = MemUtil.BytesToUInt32(pbKey32, 20);
m_s[10] = MemUtil.BytesToUInt32(pbKey32, 24);
m_s[11] = MemUtil.BytesToUInt32(pbKey32, 28);
m_s[0] = g_sigma[0];
m_s[1] = g_sigma[1];
m_s[2] = g_sigma[2];
m_s[3] = g_sigma[3];
// IV setup
m_s[12] = 0; // Counter
m_s[13] = MemUtil.BytesToUInt32(pbIV12, 0);
m_s[14] = MemUtil.BytesToUInt32(pbIV12, 4);
m_s[15] = MemUtil.BytesToUInt32(pbIV12, 8);
}
protected override void Dispose(bool bDisposing)
{
if(bDisposing)
{
MemUtil.ZeroArray<uint>(m_s);
MemUtil.ZeroArray<uint>(m_x);
}
base.Dispose(bDisposing);
}
protected override void NextBlock(byte[] pBlock)
{
if(pBlock == null) throw new ArgumentNullException("pBlock");
if(pBlock.Length != 64) throw new ArgumentOutOfRangeException("pBlock");
// x is a local alias for the working buffer; with this,
// the compiler/runtime might remove some checks
uint[] x = m_x;
if(x == null) throw new InvalidOperationException();
if(x.Length < 16) throw new InvalidOperationException();
uint[] s = m_s;
if(s == null) throw new InvalidOperationException();
if(s.Length < 16) throw new InvalidOperationException();
Array.Copy(s, x, 16);
unchecked
{
// 10 * 8 quarter rounds = 20 rounds
for(int i = 0; i < 10; ++i)
{
// Column quarter rounds
x[ 0] += x[ 4];
x[12] = MemUtil.RotateLeft32(x[12] ^ x[ 0], 16);
x[ 8] += x[12];
x[ 4] = MemUtil.RotateLeft32(x[ 4] ^ x[ 8], 12);
x[ 0] += x[ 4];
x[12] = MemUtil.RotateLeft32(x[12] ^ x[ 0], 8);
x[ 8] += x[12];
x[ 4] = MemUtil.RotateLeft32(x[ 4] ^ x[ 8], 7);
x[ 1] += x[ 5];
x[13] = MemUtil.RotateLeft32(x[13] ^ x[ 1], 16);
x[ 9] += x[13];
x[ 5] = MemUtil.RotateLeft32(x[ 5] ^ x[ 9], 12);
x[ 1] += x[ 5];
x[13] = MemUtil.RotateLeft32(x[13] ^ x[ 1], 8);
x[ 9] += x[13];
x[ 5] = MemUtil.RotateLeft32(x[ 5] ^ x[ 9], 7);
x[ 2] += x[ 6];
x[14] = MemUtil.RotateLeft32(x[14] ^ x[ 2], 16);
x[10] += x[14];
x[ 6] = MemUtil.RotateLeft32(x[ 6] ^ x[10], 12);
x[ 2] += x[ 6];
x[14] = MemUtil.RotateLeft32(x[14] ^ x[ 2], 8);
x[10] += x[14];
x[ 6] = MemUtil.RotateLeft32(x[ 6] ^ x[10], 7);
x[ 3] += x[ 7];
x[15] = MemUtil.RotateLeft32(x[15] ^ x[ 3], 16);
x[11] += x[15];
x[ 7] = MemUtil.RotateLeft32(x[ 7] ^ x[11], 12);
x[ 3] += x[ 7];
x[15] = MemUtil.RotateLeft32(x[15] ^ x[ 3], 8);
x[11] += x[15];
x[ 7] = MemUtil.RotateLeft32(x[ 7] ^ x[11], 7);
// Diagonal quarter rounds
x[ 0] += x[ 5];
x[15] = MemUtil.RotateLeft32(x[15] ^ x[ 0], 16);
x[10] += x[15];
x[ 5] = MemUtil.RotateLeft32(x[ 5] ^ x[10], 12);
x[ 0] += x[ 5];
x[15] = MemUtil.RotateLeft32(x[15] ^ x[ 0], 8);
x[10] += x[15];
x[ 5] = MemUtil.RotateLeft32(x[ 5] ^ x[10], 7);
x[ 1] += x[ 6];
x[12] = MemUtil.RotateLeft32(x[12] ^ x[ 1], 16);
x[11] += x[12];
x[ 6] = MemUtil.RotateLeft32(x[ 6] ^ x[11], 12);
x[ 1] += x[ 6];
x[12] = MemUtil.RotateLeft32(x[12] ^ x[ 1], 8);
x[11] += x[12];
x[ 6] = MemUtil.RotateLeft32(x[ 6] ^ x[11], 7);
x[ 2] += x[ 7];
x[13] = MemUtil.RotateLeft32(x[13] ^ x[ 2], 16);
x[ 8] += x[13];
x[ 7] = MemUtil.RotateLeft32(x[ 7] ^ x[ 8], 12);
x[ 2] += x[ 7];
x[13] = MemUtil.RotateLeft32(x[13] ^ x[ 2], 8);
x[ 8] += x[13];
x[ 7] = MemUtil.RotateLeft32(x[ 7] ^ x[ 8], 7);
x[ 3] += x[ 4];
x[14] = MemUtil.RotateLeft32(x[14] ^ x[ 3], 16);
x[ 9] += x[14];
x[ 4] = MemUtil.RotateLeft32(x[ 4] ^ x[ 9], 12);
x[ 3] += x[ 4];
x[14] = MemUtil.RotateLeft32(x[14] ^ x[ 3], 8);
x[ 9] += x[14];
x[ 4] = MemUtil.RotateLeft32(x[ 4] ^ x[ 9], 7);
}
for(int i = 0; i < 16; ++i) x[i] += s[i];
for(int i = 0; i < 16; ++i)
{
int i4 = i << 2;
uint xi = x[i];
pBlock[i4] = (byte)xi;
pBlock[i4 + 1] = (byte)(xi >> 8);
pBlock[i4 + 2] = (byte)(xi >> 16);
pBlock[i4 + 3] = (byte)(xi >> 24);
}
++s[12];
if(s[12] == 0)
{
if(!m_bLargeCounter)
throw new InvalidOperationException(
KLRes.EncDataTooLarge.Replace(@"{PARAM}", StrNameRfc));
++s[13]; // Increment high half of large counter
}
}
}
public long Seek(long lOffset, SeekOrigin so)
{
if(so != SeekOrigin.Begin) throw new NotSupportedException();
if((lOffset < 0) || ((lOffset & 63) != 0) ||
((lOffset >> 6) > (long)uint.MaxValue))
throw new ArgumentOutOfRangeException("lOffset");
m_s[12] = (uint)(lOffset >> 6);
InvalidateBlock();
return lOffset;
}
}
}

View File

@@ -0,0 +1,176 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2019 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
using System;
using System.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 s, byte[] pbKey, byte[] pbIV)
{
return new ChaCha20Stream(s, true, pbKey, pbIV);
}
public Stream DecryptStream(Stream s, byte[] pbKey, byte[] pbIV)
{
return new ChaCha20Stream(s, false, pbKey, pbIV);
}
}
public 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.Dispose();
m_sBase = null;
}
m_pbBuffer = null;
}
base.Dispose(bDisposing);
}
public override void Flush()
{
Debug.Assert(m_sBase != null);
if(m_bWriting && (m_sBase != null)) m_sBase.Flush();
}
public override long Seek(long lOffset, SeekOrigin soOrigin)
{
Debug.Assert(false);
throw new NotImplementedException();
}
public override void SetLength(long lValue)
{
Debug.Assert(false);
throw new NotImplementedException();
}
public override int Read(byte[] pbBuffer, int iOffset, int nCount)
{
if(m_bWriting) throw new InvalidOperationException();
int cbRead = m_sBase.Read(pbBuffer, iOffset, nCount);
m_c.Decrypt(pbBuffer, iOffset, cbRead);
return cbRead;
}
public override void Write(byte[] pbBuffer, int iOffset, int nCount)
{
if(nCount < 0) throw new ArgumentOutOfRangeException("nCount");
if(nCount == 0) return;
if(!m_bWriting) throw new InvalidOperationException();
if((m_pbBuffer == null) || (m_pbBuffer.Length < nCount))
m_pbBuffer = new byte[nCount];
Array.Copy(pbBuffer, iOffset, m_pbBuffer, 0, nCount);
m_c.Encrypt(m_pbBuffer, 0, nCount);
m_sBase.Write(m_pbBuffer, 0, nCount);
}
}
}

View File

@@ -0,0 +1,165 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2019 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;
namespace ModernKeePassLib.Cryptography.Cipher
{
/// <summary>
/// Pool of encryption/decryption algorithms (ciphers).
/// </summary>
public sealed class CipherPool
{
private List<ICipherEngine> m_lCiphers = new List<ICipherEngine>();
private static CipherPool m_poolGlobal = null;
public static CipherPool GlobalPool
{
get
{
CipherPool cp = m_poolGlobal;
if(cp == null)
{
cp = new CipherPool();
cp.AddCipher(new StandardAesEngine());
cp.AddCipher(new ChaCha20Engine());
m_poolGlobal = cp;
}
return cp;
}
}
/// <summary>
/// Remove all cipher engines from the current pool.
/// </summary>
public void Clear()
{
m_lCiphers.Clear();
}
/// <summary>
/// Add a cipher engine to the pool.
/// </summary>
/// <param name="c">Cipher engine to add. Must not be <c>null</c>.</param>
public void AddCipher(ICipherEngine c)
{
if(c == null) { Debug.Assert(false); throw new ArgumentNullException("c"); }
// Return if a cipher with that ID is registered already
foreach(ICipherEngine cEx in m_lCiphers)
{
if(cEx.CipherUuid.Equals(c.CipherUuid))
return;
}
m_lCiphers.Add(c);
}
/// <summary>
/// Get a cipher identified by its UUID.
/// </summary>
/// <param name="uuidCipher">UUID of the cipher to return.</param>
/// <returns>Reference to the requested cipher. If the cipher is
/// not found, <c>null</c> is returned.</returns>
public ICipherEngine GetCipher(PwUuid uuidCipher)
{
foreach(ICipherEngine c in m_lCiphers)
{
if(c.CipherUuid.Equals(uuidCipher))
return c;
}
return null;
}
/// <summary>
/// Get the index of a cipher. This index is temporary and should
/// not be stored or used to identify a cipher.
/// </summary>
/// <param name="uuidCipher">UUID of the cipher.</param>
/// <returns>Index of the requested cipher. Returns <c>-1</c> if
/// the specified cipher is not found.</returns>
public int GetCipherIndex(PwUuid uuidCipher)
{
for(int i = 0; i < m_lCiphers.Count; ++i)
{
if(m_lCiphers[i].CipherUuid.Equals(uuidCipher))
return i;
}
Debug.Assert(false);
return -1;
}
/// <summary>
/// Get the index of a cipher. This index is temporary and should
/// not be stored or used to identify a cipher.
/// </summary>
/// <param name="strDisplayName">Name of the cipher. Note that
/// multiple ciphers can have the same name. In this case, the
/// first matching cipher is returned.</param>
/// <returns>Cipher with the specified name or <c>-1</c> if
/// no cipher with that name is found.</returns>
public int GetCipherIndex(string strDisplayName)
{
for(int i = 0; i < m_lCiphers.Count; ++i)
{
if(m_lCiphers[i].DisplayName == strDisplayName)
return i;
}
Debug.Assert(false);
return -1;
}
/// <summary>
/// Get the number of cipher engines in this pool.
/// </summary>
public int EngineCount
{
get { return m_lCiphers.Count; }
}
/// <summary>
/// Get the cipher engine at the specified position. Throws
/// an exception if the index is invalid. You can use this
/// to iterate over all ciphers, but do not use it to
/// identify ciphers.
/// </summary>
/// <param name="nIndex">Index of the requested cipher engine.</param>
/// <returns>Reference to the cipher engine at the specified
/// position.</returns>
public ICipherEngine this[int nIndex]
{
get
{
if((nIndex < 0) || (nIndex >= m_lCiphers.Count))
throw new ArgumentOutOfRangeException("nIndex");
return m_lCiphers[nIndex];
}
}
}
}

View File

@@ -0,0 +1,109 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2019 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using ModernKeePassLib.Utility;
namespace ModernKeePassLib.Cryptography.Cipher
{
public abstract class CtrBlockCipher : IDisposable
{
private bool m_bDisposed = false;
private byte[] m_pBlock;
private int m_iBlockPos;
public abstract int BlockSize
{
get;
}
public CtrBlockCipher()
{
int cb = this.BlockSize;
if(cb <= 0) throw new InvalidOperationException("this.BlockSize");
m_pBlock = new byte[cb];
m_iBlockPos = cb;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool bDisposing)
{
if(bDisposing)
{
MemUtil.ZeroByteArray(m_pBlock);
m_iBlockPos = m_pBlock.Length;
m_bDisposed = true;
}
}
protected void InvalidateBlock()
{
m_iBlockPos = m_pBlock.Length;
}
protected abstract void NextBlock(byte[] pBlock);
public void Encrypt(byte[] m, int iOffset, int cb)
{
if(m_bDisposed) throw new ObjectDisposedException(null);
if(m == null) throw new ArgumentNullException("m");
if(iOffset < 0) throw new ArgumentOutOfRangeException("iOffset");
if(cb < 0) throw new ArgumentOutOfRangeException("cb");
if(iOffset > (m.Length - cb)) throw new ArgumentOutOfRangeException("cb");
int cbBlock = m_pBlock.Length;
while(cb > 0)
{
Debug.Assert(m_iBlockPos <= cbBlock);
if(m_iBlockPos == cbBlock)
{
NextBlock(m_pBlock);
m_iBlockPos = 0;
}
int cbCopy = Math.Min(cbBlock - m_iBlockPos, cb);
Debug.Assert(cbCopy > 0);
MemUtil.XorArray(m_pBlock, m_iBlockPos, m, iOffset, cbCopy);
m_iBlockPos += cbCopy;
iOffset += cbCopy;
cb -= cbCopy;
}
}
public void Decrypt(byte[] m, int iOffset, int cb)
{
Encrypt(m, iOffset, cb);
}
}
}

View File

@@ -0,0 +1,69 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2019 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
using System;
using System.IO;
namespace ModernKeePassLib.Cryptography.Cipher
{
public interface ICipherEngine
{
/// <summary>
/// UUID of the engine. If you want to write an engine/plugin,
/// please contact the KeePass team to obtain a new UUID.
/// </summary>
PwUuid CipherUuid
{
get;
}
/// <summary>
/// Name displayed in the list of available encryption/decryption
/// engines in the GUI.
/// </summary>
string DisplayName
{
get;
}
Stream EncryptStream(Stream s, byte[] pbKey, byte[] pbIV);
Stream DecryptStream(Stream s, byte[] pbKey, byte[] pbIV);
}
public interface ICipherEngine2 : ICipherEngine
{
/// <summary>
/// Length of an encryption key in bytes.
/// The base <c>ICipherEngine</c> assumes 32.
/// </summary>
int KeyLength
{
get;
}
/// <summary>
/// Length of the initialization vector in bytes.
/// The base <c>ICipherEngine</c> assumes 16.
/// </summary>
int IVLength
{
get;
}
}
}

View File

@@ -0,0 +1,165 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2019 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
// 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 : CtrBlockCipher
{
private uint[] m_s = new uint[16]; // State
private uint[] m_x = new uint[16]; // Working buffer
private static readonly uint[] g_sigma = new uint[4] {
0x61707865, 0x3320646E, 0x79622D32, 0x6B206574
};
public override int BlockSize
{
get { return 64; }
}
public Salsa20Cipher(byte[] pbKey32, byte[] pbIV8) : base()
{
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
}
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)
{
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] += 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[8];
if(s[8] == 0) ++s[9];
}
}
}
}

View File

@@ -0,0 +1,133 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2019 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Security;
using System.Text;
#if !KeePassUAP
using System.Security.Cryptography;
#endif
using ModernKeePassLib.Resources;
namespace ModernKeePassLib.Cryptography.Cipher
{
public sealed class StandardAesEngine : ICipherEngine
{
#if !KeePassUAP
private const CipherMode SaeCipherMode = CipherMode.CBC;
private const PaddingMode SaePaddingMode = PaddingMode.PKCS7;
#endif
private static PwUuid g_uuidAes = null;
public static PwUuid AesUuid
{
get
{
PwUuid pu = g_uuidAes;
if(pu == null)
{
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 pu;
}
}
public PwUuid CipherUuid
{
get { return StandardAesEngine.AesUuid; }
}
public string DisplayName
{
get
{
return ("AES/Rijndael (" + KLRes.KeyBits.Replace(@"{PARAM}",
"256") + ", FIPS 197)");
}
}
private static void ValidateArguments(Stream s, bool bEncrypt, byte[] pbKey, byte[] pbIV)
{
if(s == null) { Debug.Assert(false); throw new ArgumentNullException("s"); }
if(pbKey == null) { Debug.Assert(false); throw new ArgumentNullException("pbKey"); }
if(pbKey.Length != 32) { Debug.Assert(false); throw new ArgumentOutOfRangeException("pbKey"); }
if(pbIV == null) { Debug.Assert(false); throw new ArgumentNullException("pbIV"); }
if(pbIV.Length != 16) { Debug.Assert(false); throw new ArgumentOutOfRangeException("pbIV"); }
if(bEncrypt)
{
Debug.Assert(s.CanWrite);
if(!s.CanWrite) throw new ArgumentException("Stream must be writable!");
}
else // Decrypt
{
Debug.Assert(s.CanRead);
if(!s.CanRead) throw new ArgumentException("Stream must be readable!");
}
}
private static Stream CreateStream(Stream s, bool bEncrypt, byte[] pbKey, byte[] pbIV)
{
StandardAesEngine.ValidateArguments(s, bEncrypt, pbKey, pbIV);
#if KeePassUAP
return StandardAesEngineExt.CreateStream(s, bEncrypt, pbKey, pbIV);
#else
SymmetricAlgorithm a = CryptoUtil.CreateAes();
if(a.BlockSize != 128) // AES block size
{
Debug.Assert(false);
a.BlockSize = 128;
}
a.KeySize = 256;
a.Mode = SaeCipherMode;
a.Padding = SaePaddingMode;
ICryptoTransform t;
if(bEncrypt) t = a.CreateEncryptor(pbKey, pbIV);
else t = a.CreateDecryptor(pbKey, pbIV);
if(t == null) { Debug.Assert(false); throw new SecurityException("Unable to create AES transform!"); }
return new CryptoStreamEx(s, t, bEncrypt ? CryptoStreamMode.Write :
CryptoStreamMode.Read, a);
#endif
}
public Stream EncryptStream(Stream s, byte[] pbKey, byte[] pbIV)
{
return StandardAesEngine.CreateStream(s, true, pbKey, pbIV);
}
public Stream DecryptStream(Stream s, byte[] pbKey, byte[] pbIV)
{
return StandardAesEngine.CreateStream(s, false, pbKey, pbIV);
}
}
}

View File

@@ -0,0 +1,387 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2019 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
#if !KeePassUAP
using System.Drawing;
using System.Security.Cryptography;
#endif
#if !ModernKeePassLib
using System.Windows.Forms;
#endif
using ModernKeePassLib.Delegates;
using ModernKeePassLib.Native;
using ModernKeePassLib.Security;
using ModernKeePassLib.Utility;
namespace ModernKeePassLib.Cryptography
{
/// <summary>
/// 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 ProtectedBinary m_pbEntropyPool = new ProtectedBinary(
true, new byte[64]);
private RNGCryptoServiceProvider m_rng = new RNGCryptoServiceProvider();
private ulong m_uCounter;
private ulong m_uGeneratedBytesCount = 0;
private static readonly object g_oSyncRoot = new object();
private readonly object m_oSyncRoot = new object();
private static CryptoRandom g_pInstance = null;
public static CryptoRandom Instance
{
get
{
CryptoRandom cr;
lock(g_oSyncRoot)
{
cr = g_pInstance;
if(cr == null)
{
cr = new CryptoRandom();
g_pInstance = cr;
}
}
return cr;
}
}
/// <summary>
/// Get the number of random bytes that this instance generated so far.
/// Note that this number can be higher than the number of random bytes
/// actually requested using the <c>GetRandomBytes</c> method.
/// </summary>
public ulong GeneratedBytesCount
{
get
{
ulong u;
lock(m_oSyncRoot) { u = m_uGeneratedBytesCount; }
return u;
}
}
/// <summary>
/// Event that is triggered whenever the internal <c>GenerateRandom256</c>
/// method is called to generate random bytes.
/// </summary>
public event EventHandler GenerateRandom256Pre;
private CryptoRandom()
{
m_uCounter = (ulong)DateTime.UtcNow.ToBinary();
byte[] pb = GetSystemEntropy();
AddEntropy(pb);
MemUtil.ZeroByteArray(pb);
}
/// <summary>
/// Update the internal seed of the random number generator based
/// on entropy data.
/// This method is thread-safe.
/// </summary>
/// <param name="pbEntropy">Entropy bytes.</param>
public void AddEntropy(byte[] pbEntropy)
{
if(pbEntropy == null) { Debug.Assert(false); return; }
if(pbEntropy.Length == 0) { Debug.Assert(false); return; }
byte[] pbNewData = pbEntropy;
if(pbEntropy.Length > 64)
{
#if KeePassLibSD
using(SHA256Managed shaNew = new SHA256Managed())
#else
using(SHA512Managed shaNew = new SHA512Managed())
#endif
{
pbNewData = shaNew.ComputeHash(pbEntropy);
}
}
lock(m_oSyncRoot)
{
byte[] pbPool = m_pbEntropyPool.ReadData();
int cbPool = pbPool.Length;
int cbNew = pbNewData.Length;
byte[] pbCmp = new byte[cbPool + cbNew];
Array.Copy(pbPool, pbCmp, cbPool);
Array.Copy(pbNewData, 0, pbCmp, cbPool, cbNew);
#if KeePassLibSD
using(SHA256Managed shaPool = new SHA256Managed())
#else
using(SHA512Managed shaPool = new SHA512Managed())
#endif
{
byte[] pbNewPool = shaPool.ComputeHash(pbCmp);
m_pbEntropyPool = new ProtectedBinary(true, pbNewPool);
MemUtil.ZeroByteArray(pbNewPool);
}
MemUtil.ZeroByteArray(pbCmp);
MemUtil.ZeroByteArray(pbPool);
}
if(pbNewData != pbEntropy) MemUtil.ZeroByteArray(pbNewData);
}
private byte[] GetSystemEntropy()
{
SHA512Managed h = new SHA512Managed();
byte[] pb4 = new byte[4];
byte[] pb8 = new byte[8];
GAction<byte[], bool> f = delegate(byte[] pbValue, bool bClearValue)
{
if(pbValue == null) { Debug.Assert(false); return; }
if(pbValue.Length == 0) return;
h.TransformBlock(pbValue, 0, pbValue.Length, pbValue, 0);
if(bClearValue) MemUtil.ZeroByteArray(pbValue);
};
Action<int> fI32 = delegate(int iValue)
{
MemUtil.Int32ToBytesEx(iValue, pb4, 0);
f(pb4, false);
};
Action<long> fI64 = delegate(long lValue)
{
MemUtil.Int64ToBytesEx(lValue, pb8, 0);
f(pb8, false);
};
Action<string> fStr = delegate(string strValue)
{
if(strValue == null) { Debug.Assert(false); return; }
if(strValue.Length == 0) return;
f(StrUtil.Utf8.GetBytes(strValue), false);
};
fI32(Environment.TickCount);
fI64(DateTime.UtcNow.ToBinary());
#if !KeePassLibSD && !ModernKeePassLib
// In try-catch for systems without GUI;
// https://sourceforge.net/p/keepass/discussion/329221/thread/20335b73/
try
{
Point pt = Cursor.Position;
fI32(pt.X);
fI32(pt.Y);
}
catch(Exception) { Debug.Assert(NativeLib.IsUnix()); }
#endif
try
{
fI32((int)NativeLib.GetPlatformID());
#if KeePassUAP
fStr(EnvironmentExt.OSVersion.VersionString);
#else
fStr(Environment.OSVersion.VersionString);
#endif
fI32(Environment.ProcessorCount);
#if !KeePassUAP
fStr(Environment.CommandLine);
fI64(Environment.WorkingSet);
#endif
}
catch(Exception) { Debug.Assert(false); }
try
{
foreach(DictionaryEntry de in Environment.GetEnvironmentVariables())
{
fStr(de.Key as string);
fStr(de.Value as string);
}
}
catch(Exception) { Debug.Assert(false); }
try
{
#if KeePassUAP
f(DiagnosticsExt.GetProcessEntropy(), true);
#elif !KeePassLibSD
using(Process p = Process.GetCurrentProcess())
{
fI64(p.Handle.ToInt64());
fI32(p.HandleCount);
fI32(p.Id);
fI64(p.NonpagedSystemMemorySize64);
fI64(p.PagedMemorySize64);
fI64(p.PagedSystemMemorySize64);
fI64(p.PeakPagedMemorySize64);
fI64(p.PeakVirtualMemorySize64);
fI64(p.PeakWorkingSet64);
fI64(p.PrivateMemorySize64);
fI64(p.StartTime.ToBinary());
fI64(p.VirtualMemorySize64);
fI64(p.WorkingSet64);
// Not supported in Mono 1.2.6:
// fI32(p.SessionId);
}
#endif
}
catch(Exception) { Debug.Assert(NativeLib.IsUnix()); }
try
{
CultureInfo ci = CultureInfo.CurrentCulture;
if(ci != null) fI32(ci.GetHashCode());
else { Debug.Assert(false); }
}
catch(Exception) { Debug.Assert(false); }
f(Guid.NewGuid().ToByteArray(), false);
f(GetCspRandom(), true);
h.TransformFinalBlock(MemUtil.EmptyByteArray, 0, 0);
byte[] pbHash = h.Hash;
h.Clear();
MemUtil.ZeroByteArray(pb4);
MemUtil.ZeroByteArray(pb8);
return pbHash;
}
private byte[] GetCspRandom()
{
byte[] pb = new byte[32];
try { m_rng.GetBytes(pb); }
catch(Exception)
{
Debug.Assert(false);
MemUtil.Int64ToBytesEx(DateTime.UtcNow.ToBinary(), pb, 0);
}
return pb;
}
private byte[] GenerateRandom256()
{
if(this.GenerateRandom256Pre != null)
this.GenerateRandom256Pre(this, EventArgs.Empty);
byte[] pbCmp;
lock(m_oSyncRoot)
{
m_uCounter += 0x74D8B29E4D38E161UL; // Prime number
byte[] pbCounter = MemUtil.UInt64ToBytes(m_uCounter);
byte[] pbCsp = GetCspRandom();
byte[] pbPool = m_pbEntropyPool.ReadData();
int cbPool = pbPool.Length;
int cbCtr = pbCounter.Length;
int cbCsp = pbCsp.Length;
pbCmp = new byte[cbPool + cbCtr + cbCsp];
Array.Copy(pbPool, pbCmp, cbPool);
Array.Copy(pbCounter, 0, pbCmp, cbPool, cbCtr);
Array.Copy(pbCsp, 0, pbCmp, cbPool + cbCtr, cbCsp);
MemUtil.ZeroByteArray(pbCsp);
MemUtil.ZeroByteArray(pbPool);
m_uGeneratedBytesCount += 32;
}
byte[] pbRet = CryptoUtil.HashSha256(pbCmp);
MemUtil.ZeroByteArray(pbCmp);
return pbRet;
}
/// <summary>
/// Get a number of cryptographically strong random bytes.
/// This method is thread-safe.
/// </summary>
/// <param name="uRequestedBytes">Number of requested random bytes.</param>
/// <returns>A byte array consisting of <paramref name="uRequestedBytes" />
/// random bytes.</returns>
public byte[] GetRandomBytes(uint uRequestedBytes)
{
if(uRequestedBytes == 0) return MemUtil.EmptyByteArray;
if(uRequestedBytes > (uint)int.MaxValue)
{
Debug.Assert(false);
throw new ArgumentOutOfRangeException("uRequestedBytes");
}
int cbRem = (int)uRequestedBytes;
byte[] pbRes = new byte[cbRem];
int iPos = 0;
while(cbRem != 0)
{
byte[] pbRandom256 = GenerateRandom256();
Debug.Assert(pbRandom256.Length == 32);
int cbCopy = Math.Min(cbRem, pbRandom256.Length);
Array.Copy(pbRandom256, 0, pbRes, iPos, cbCopy);
MemUtil.ZeroByteArray(pbRandom256);
iPos += cbCopy;
cbRem -= cbCopy;
}
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);
}
}
}

View File

@@ -0,0 +1,262 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2019 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
using System;
using System.Diagnostics;
#if !KeePassUAP
using System.Security.Cryptography;
#endif
using ModernKeePassLib.Cryptography.Cipher;
using ModernKeePassLib.Utility;
namespace ModernKeePassLib.Cryptography
{
/// <summary>
/// Algorithms supported by <c>CryptoRandomStream</c>.
/// </summary>
public enum CrsAlgorithm
{
/// <summary>
/// Not supported.
/// </summary>
Null = 0,
/// <summary>
/// A variant of the ARCFour algorithm (RC4 incompatible).
/// Insecure; for backward compatibility only.
/// </summary>
ArcFourVariant = 1,
/// <summary>
/// Salsa20 stream cipher algorithm.
/// </summary>
Salsa20 = 2,
/// <summary>
/// ChaCha20 stream cipher algorithm.
/// </summary>
ChaCha20 = 3,
Count = 4
}
/// <summary>
/// A random stream class. The class is initialized using random
/// bytes provided by the caller. The produced stream has random
/// 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 : IDisposable
{
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="a">Algorithm to use.</param>
/// <param name="pbKey">Initialization key. Must not be <c>null</c>
/// and must contain at least 1 byte.</param>
public CryptoRandomStream(CrsAlgorithm a, byte[] pbKey)
{
if(pbKey == null) { Debug.Assert(false); throw new ArgumentNullException("pbKey"); }
int cbKey = pbKey.Length;
if(cbKey <= 0)
{
Debug.Assert(false); // Need at least one byte
throw new ArgumentOutOfRangeException("pbKey");
}
m_crsAlgorithm = a;
if(a == CrsAlgorithm.ChaCha20)
{
byte[] pbKey32 = new byte[32];
byte[] pbIV12 = new byte[12];
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);
}
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(int w = 0; w < 256; ++w) m_pbState[w] = (byte)w;
unchecked
{
byte j = 0, t;
int inxKey = 0;
for(int w = 0; w < 256; ++w) // Key setup
{
j += (byte)(m_pbState[w] + pbKey[inxKey]);
t = m_pbState[0]; // Swap entries
m_pbState[0] = m_pbState[j];
m_pbState[j] = t;
++inxKey;
if(inxKey >= cbKey) inxKey = 0;
}
}
GetRandomBytes(512); // Increases security, see cryptanalysis
}
else // Unknown algorithm
{
Debug.Assert(false);
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;
}
}
/// <summary>
/// Get <paramref name="uRequestedCount" /> random bytes.
/// </summary>
/// <param name="uRequestedCount">Number of random bytes to retrieve.</param>
/// <returns>Returns <paramref name="uRequestedCount" /> random bytes.</returns>
public byte[] GetRandomBytes(uint uRequestedCount)
{
if(m_bDisposed) throw new ObjectDisposedException(null);
if(uRequestedCount == 0) return MemUtil.EmptyByteArray;
if(uRequestedCount > (uint)int.MaxValue)
throw new ArgumentOutOfRangeException("uRequestedCount");
int cb = (int)uRequestedCount;
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(int w = 0; w < cb; ++w)
{
++m_i;
m_j += m_pbState[m_i];
byte t = m_pbState[m_i]; // Swap entries
m_pbState[m_i] = m_pbState[m_j];
m_pbState[m_j] = t;
t = (byte)(m_pbState[m_i] + m_pbState[m_j]);
pbRet[w] = m_pbState[t];
}
}
}
else { Debug.Assert(false); }
return pbRet;
}
public ulong GetRandomUInt64()
{
byte[] pb = GetRandomBytes(8);
return MemUtil.BytesToUInt64(pb);
}
#if CRSBENCHMARK
public static string Benchmark()
{
int nRounds = 2000000;
string str = "ArcFour small: " + BenchTime(CrsAlgorithm.ArcFourVariant,
nRounds, 16).ToString() + "\r\n";
str += "ArcFour big: " + BenchTime(CrsAlgorithm.ArcFourVariant,
32, 2 * 1024 * 1024).ToString() + "\r\n";
str += "Salsa20 small: " + BenchTime(CrsAlgorithm.Salsa20,
nRounds, 16).ToString() + "\r\n";
str += "Salsa20 big: " + BenchTime(CrsAlgorithm.Salsa20,
32, 2 * 1024 * 1024).ToString();
return str;
}
private static int BenchTime(CrsAlgorithm cra, int nRounds, int nDataSize)
{
byte[] pbKey = new byte[4] { 0x00, 0x01, 0x02, 0x03 };
int nStart = Environment.TickCount;
for(int i = 0; i < nRounds; ++i)
{
using(CryptoRandomStream c = new CryptoRandomStream(cra, pbKey))
{
c.GetRandomBytes((uint)nDataSize);
}
}
int nEnd = Environment.TickCount;
return (nEnd - nStart);
}
#endif
}
}

View File

@@ -0,0 +1,64 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2019 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;
#if !KeePassUAP
using System.Security.Cryptography;
namespace ModernKeePassLib.Cryptography
{
public sealed class CryptoStreamEx : CryptoStream
{
private ICryptoTransform m_t;
private SymmetricAlgorithm m_a;
public CryptoStreamEx(Stream s, ICryptoTransform t, CryptoStreamMode m,
SymmetricAlgorithm a) : base(s, t, m)
{
m_t = t;
m_a = a;
}
protected override void Dispose(bool disposing)
{
try { base.Dispose(disposing); }
// Unnecessary exception from CryptoStream with
// RijndaelManagedTransform when a stream hasn't been
// read completely (e.g. incorrect master key)
catch (CryptographicException) { }
catch (Exception) { Debug.Assert(false); }
if (disposing)
{
try { if (m_t != null) { m_t.Dispose(); m_t = null; } }
catch (Exception) { Debug.Assert(false); }
// In .NET 2.0, SymmetricAlgorithm.Dispose() is not public
try { if (m_a != null) { m_a.Clear(); m_a = null; } }
catch (Exception) { Debug.Assert(false); }
}
}
}
}
#endif

View File

@@ -0,0 +1,254 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2019 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Text;
#if !KeePassUAP
using System.Security.Cryptography;
#endif
using ModernKeePassLib.Native;
using ModernKeePassLib.Utility;
namespace ModernKeePassLib.Cryptography
{
public static class CryptoUtil
{
private static bool? g_obProtData = null;
public static bool IsProtectedDataSupported
{
get
{
if(g_obProtData.HasValue) return g_obProtData.Value;
bool b = false;
try
{
Random r = CryptoRandom.NewWeakRandom();
byte[] pbData = new byte[137];
r.NextBytes(pbData);
byte[] pbEnt = new byte[41];
r.NextBytes(pbEnt);
byte[] pbEnc = ProtectedData.Protect(pbData, pbEnt,
DataProtectionScope.CurrentUser);
if((pbEnc != null) && !MemUtil.ArraysEqual(pbEnc, pbData))
{
byte[] pbDec = ProtectedData.Unprotect(pbEnc, pbEnt,
DataProtectionScope.CurrentUser);
if((pbDec != null) && MemUtil.ArraysEqual(pbDec, pbData))
b = true;
}
}
catch(Exception) { Debug.Assert(false); }
Debug.Assert(b); // Should be supported on all systems
g_obProtData = b;
return b;
}
}
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;
using(SHA256Managed h = new SHA256Managed())
{
pbHash = h.ComputeHash(pbData, iOffset, cbCount);
}
#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;
}
internal static byte[] HashSha256(string strFilePath)
{
byte[] pbHash = null;
using(FileStream fs = new FileStream(strFilePath, FileMode.Open,
FileAccess.Read, FileShare.Read))
{
using(SHA256Managed h = new SHA256Managed())
{
pbHash = h.ComputeHash(fs);
}
}
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
{
using(SHA512Managed h = new SHA512Managed())
{
pbHash = h.ComputeHash(pbIn, iInOffset, cbIn);
}
}
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);
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);
}
}
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 !KeePassUAP
private static bool? g_obAesCsp = null;
public 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
public static byte[] ProtectData(byte[] pb, byte[] pbOptEntropy,
DataProtectionScope s)
{
return ProtectDataPriv(pb, true, pbOptEntropy, s);
}
public static byte[] UnprotectData(byte[] pb, byte[] pbOptEntropy,
DataProtectionScope s)
{
return ProtectDataPriv(pb, false, pbOptEntropy, s);
}
private static byte[] ProtectDataPriv(byte[] pb, bool bProtect,
byte[] pbOptEntropy, DataProtectionScope s)
{
if(pb == null) throw new ArgumentNullException("pb");
if((pbOptEntropy != null) && (pbOptEntropy.Length == 0))
pbOptEntropy = null;
if(CryptoUtil.IsProtectedDataSupported)
{
if(bProtect)
return ProtectedData.Protect(pb, pbOptEntropy, s);
return ProtectedData.Unprotect(pb, pbOptEntropy, s);
}
Debug.Assert(false);
byte[] pbCopy = new byte[pb.Length];
Array.Copy(pb, pbCopy, pb.Length);
return pbCopy;
}
}
}

View File

@@ -0,0 +1,232 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2019 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
// 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;
#if !KeePassUAP
using System.Security.Cryptography;
#endif
using ModernKeePassLib.Utility;
namespace ModernKeePassLib.Cryptography.Hash
{
public sealed class Blake2b : HashAlgorithm
{
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 Blake2b()
{
m_cbHashLength = NbMaxOutBytes;
this.HashSizeValue = NbMaxOutBytes * 8; // Bits
Initialize();
}
public Blake2b(int cbHashLength)
{
if((cbHashLength < 0) || (cbHashLength > NbMaxOutBytes))
throw new ArgumentOutOfRangeException("cbHashLength");
m_cbHashLength = cbHashLength;
this.HashSizeValue = cbHashLength * 8; // Bits
Initialize();
}
public override 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];
}
protected override void HashCore(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;
}
}
protected override byte[] HashFinal()
{
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) return pbHash;
Debug.Assert(m_cbHashLength < NbMaxOutBytes);
byte[] pbShort = new byte[m_cbHashLength];
if(m_cbHashLength > 0)
Array.Copy(pbHash, pbShort, m_cbHashLength);
MemUtil.ZeroByteArray(pbHash);
return pbShort;
}
}
}

View File

@@ -0,0 +1,187 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2019 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;
#if !KeePassUAP
using System.Security.Cryptography;
#endif
using ModernKeePassLib.Utility;
namespace ModernKeePassLib.Cryptography
{
public sealed class HashingStreamEx : Stream
{
private readonly Stream m_sBaseStream;
private readonly bool m_bWriting;
private HashAlgorithm m_hash;
private byte[] m_pbFinalHash = null;
public byte[] Hash
{
get { return m_pbFinalHash; }
}
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 { return m_sBaseStream.Length; }
}
public override long Position
{
get { return m_sBaseStream.Position; }
set { Debug.Assert(false); throw new NotSupportedException(); }
}
public HashingStreamEx(Stream sBaseStream, bool bWriting, HashAlgorithm hashAlgorithm)
{
if(sBaseStream == null) throw new ArgumentNullException("sBaseStream");
m_sBaseStream = sBaseStream;
m_bWriting = bWriting;
#if !KeePassLibSD
m_hash = (hashAlgorithm ?? new SHA256Managed());
#else // KeePassLibSD
m_hash = null;
try { m_hash = HashAlgorithm.Create("SHA256"); }
catch(Exception) { }
try { if(m_hash == null) m_hash = HashAlgorithm.Create(); }
catch(Exception) { }
#endif
if(m_hash == null) { Debug.Assert(false); return; }
// Validate hash algorithm
if(!m_hash.CanReuseTransform || !m_hash.CanTransformMultipleBlocks)
{
Debug.Assert(false);
m_hash = null;
}
}
protected override void Dispose(bool disposing)
{
if(disposing)
{
if(m_hash != null)
{
try
{
m_hash.TransformFinalBlock(MemUtil.EmptyByteArray, 0, 0);
m_pbFinalHash = m_hash.Hash;
m_hash.Clear();
}
catch(Exception) { Debug.Assert(false); }
m_hash = null;
}
m_sBaseStream.Dispose();
}
base.Dispose(disposing);
}
public override void Flush()
{
m_sBaseStream.Flush();
}
public override long Seek(long lOffset, SeekOrigin soOrigin)
{
Debug.Assert(false);
throw new NotSupportedException();
}
public override void SetLength(long lValue)
{
Debug.Assert(false);
throw new NotSupportedException();
}
public override int Read(byte[] pbBuffer, int nOffset, int nCount)
{
if(m_bWriting) { Debug.Assert(false); throw new InvalidOperationException(); }
int nRead = m_sBaseStream.Read(pbBuffer, nOffset, nCount);
int nPartialRead = nRead;
while((nRead < nCount) && (nPartialRead != 0))
{
nPartialRead = m_sBaseStream.Read(pbBuffer, nOffset + nRead,
nCount - nRead);
nRead += nPartialRead;
}
#if DEBUG
byte[] pbOrg = new byte[pbBuffer.Length];
Array.Copy(pbBuffer, pbOrg, pbBuffer.Length);
#endif
if((m_hash != null) && (nRead > 0))
m_hash.TransformBlock(pbBuffer, nOffset, nRead, pbBuffer, nOffset);
#if DEBUG
Debug.Assert(MemUtil.ArraysEqual(pbBuffer, pbOrg));
#endif
return nRead;
}
public override void Write(byte[] pbBuffer, int nOffset, int nCount)
{
if(!m_bWriting) { Debug.Assert(false); throw new InvalidOperationException(); }
#if DEBUG
byte[] pbOrg = new byte[pbBuffer.Length];
Array.Copy(pbBuffer, pbOrg, pbBuffer.Length);
#endif
if((m_hash != null) && (nCount > 0))
m_hash.TransformBlock(pbBuffer, nOffset, nCount, pbBuffer, nOffset);
#if DEBUG
Debug.Assert(MemUtil.ArraysEqual(pbBuffer, pbOrg));
#endif
m_sBaseStream.Write(pbBuffer, nOffset, nCount);
}
}
}

View File

@@ -0,0 +1,98 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2019 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
#if !KeePassUAP
using System.Security.Cryptography;
#endif
using ModernKeePassLib.Utility;
#if !KeePassLibSD
namespace ModernKeePassLib.Cryptography
{
/// <summary>
/// Generate HMAC-based one-time passwords as specified in RFC 4226.
/// </summary>
public static class HmacOtp
{
private static readonly uint[] g_vDigitsPower = new uint[] { 1,
10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000 };
public static string Generate(byte[] pbSecret, ulong uFactor,
uint uCodeDigits, bool bAddChecksum, int iTruncationOffset)
{
byte[] pbText = MemUtil.UInt64ToBytes(uFactor);
Array.Reverse(pbText); // To big-endian
byte[] pbHash;
using(HMACSHA1 h = new HMACSHA1(pbSecret))
{
pbHash = h.ComputeHash(pbText);
}
uint uOffset = (uint)(pbHash[pbHash.Length - 1] & 0xF);
if((iTruncationOffset >= 0) && (iTruncationOffset < (pbHash.Length - 4)))
uOffset = (uint)iTruncationOffset;
uint uBinary = (uint)(((pbHash[uOffset] & 0x7F) << 24) |
((pbHash[uOffset + 1] & 0xFF) << 16) |
((pbHash[uOffset + 2] & 0xFF) << 8) |
(pbHash[uOffset + 3] & 0xFF));
uint uOtp = (uBinary % g_vDigitsPower[uCodeDigits]);
if(bAddChecksum)
uOtp = ((uOtp * 10) + CalculateChecksum(uOtp, uCodeDigits));
uint uDigits = (bAddChecksum ? (uCodeDigits + 1) : uCodeDigits);
return uOtp.ToString(NumberFormatInfo.InvariantInfo).PadLeft(
(int)uDigits, '0');
}
private static readonly uint[] g_vDoubleDigits = new uint[] {
0, 2, 4, 6, 8, 1, 3, 5, 7, 9 };
private static uint CalculateChecksum(uint uNum, uint uDigits)
{
bool bDoubleDigit = true;
uint uTotal = 0;
while(0 < uDigits--)
{
uint uDigit = (uNum % 10);
uNum /= 10;
if(bDoubleDigit) uDigit = g_vDoubleDigits[uDigit];
uTotal += uDigit;
bDoubleDigit = !bDoubleDigit;
}
uint uResult = (uTotal % 10);
if(uResult != 0) uResult = 10 - uResult;
return uResult;
}
}
}
#endif

View File

@@ -0,0 +1,399 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2019 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
using System;
using System.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()
{
if(!NativeLib.IsUnix()) return false; // Independent of workaround state
if(!MonoWorkarounds.IsRequired(1468)) return false; // Can be turned off
// gcry_check_version initializes the library;
// throws when LibGCrypt is not available
NativeMethods.gcry_check_version(IntPtr.Zero);
return true;
}
// =============================================================
// Multi-threaded implementation
// For some reason, the following multi-threaded implementation
// is slower than the single-threaded implementation below
// (threading overhead by Mono? LibGCrypt threading issues?)
/* private sealed class GCryptTransformInfo : IDisposable
{
public IntPtr Data16;
public IntPtr Seed32;
public ulong Rounds;
public uint TimeMs;
public bool Success = false;
public GCryptTransformInfo(byte[] pbData32, int iDataOffset,
byte[] pbSeed32, ulong uRounds, uint uTimeMs)
{
this.Data16 = Marshal.AllocCoTaskMem(16);
Marshal.Copy(pbData32, iDataOffset, this.Data16, 16);
this.Seed32 = Marshal.AllocCoTaskMem(32);
Marshal.Copy(pbSeed32, 0, this.Seed32, 32);
this.Rounds = uRounds;
this.TimeMs = uTimeMs;
}
public void Dispose()
{
if(this.Data16 != IntPtr.Zero)
{
Marshal.WriteInt64(this.Data16, 0);
Marshal.WriteInt64(this.Data16, 8, 0);
Marshal.FreeCoTaskMem(this.Data16);
this.Data16 = IntPtr.Zero;
}
if(this.Seed32 != IntPtr.Zero)
{
Marshal.FreeCoTaskMem(this.Seed32);
this.Seed32 = IntPtr.Zero;
}
}
}
private static GCryptTransformInfo[] GCryptRun(byte[] pbData32,
byte[] pbSeed32, ulong uRounds, uint uTimeMs, ParameterizedThreadStart fL,
ParameterizedThreadStart fR)
{
GCryptTransformInfo tiL = new GCryptTransformInfo(pbData32, 0,
pbSeed32, uRounds, uTimeMs);
GCryptTransformInfo tiR = new GCryptTransformInfo(pbData32, 16,
pbSeed32, uRounds, uTimeMs);
Thread th = new Thread(fL);
th.Start(tiL);
fR(tiR);
th.Join();
Marshal.Copy(tiL.Data16, pbData32, 0, 16);
Marshal.Copy(tiR.Data16, pbData32, 16, 16);
tiL.Dispose();
tiR.Dispose();
if(tiL.Success && tiR.Success)
return new GCryptTransformInfo[2] { tiL, tiR };
return null;
}
private static bool TransformKeyGCryptPriv(byte[] pbData32, byte[] pbSeed32,
ulong uRounds)
{
return (GCryptRun(pbData32, pbSeed32, uRounds, 0,
new ParameterizedThreadStart(AesKdf.GCryptTransformTh),
new ParameterizedThreadStart(AesKdf.GCryptTransformTh)) != null);
}
private static bool GCryptInitCipher(ref IntPtr h, GCryptTransformInfo ti)
{
NativeMethods.gcry_cipher_open(ref h, NativeMethods.GCRY_CIPHER_AES256,
NativeMethods.GCRY_CIPHER_MODE_ECB, 0);
if(h == IntPtr.Zero) { Debug.Assert(false); return false; }
IntPtr n32 = new IntPtr(32);
if(NativeMethods.gcry_cipher_setkey(h, ti.Seed32, n32) != 0)
{
Debug.Assert(false);
return false;
}
return true;
}
private static void GCryptTransformTh(object o)
{
IntPtr h = IntPtr.Zero;
try
{
GCryptTransformInfo ti = (o as GCryptTransformInfo);
if(ti == null) { Debug.Assert(false); return; }
if(!GCryptInitCipher(ref h, ti)) return;
IntPtr n16 = new IntPtr(16);
for(ulong u = 0; u < ti.Rounds; ++u)
{
if(NativeMethods.gcry_cipher_encrypt(h, ti.Data16, n16,
IntPtr.Zero, IntPtr.Zero) != 0)
{
Debug.Assert(false);
return;
}
}
ti.Success = true;
}
catch(Exception) { Debug.Assert(false); }
finally
{
try { if(h != IntPtr.Zero) NativeMethods.gcry_cipher_close(h); }
catch(Exception) { Debug.Assert(false); }
}
}
private static bool TransformKeyBenchmarkGCryptPriv(uint uTimeMs, ref ulong uRounds)
{
GCryptTransformInfo[] v = GCryptRun(new byte[32], new byte[32],
0, uTimeMs,
new ParameterizedThreadStart(AesKdf.GCryptBenchmarkTh),
new ParameterizedThreadStart(AesKdf.GCryptBenchmarkTh));
if(v != null)
{
ulong uL = Math.Min(v[0].Rounds, ulong.MaxValue >> 1);
ulong uR = Math.Min(v[1].Rounds, ulong.MaxValue >> 1);
uRounds = (uL + uR) / 2;
return true;
}
return false;
}
private static void GCryptBenchmarkTh(object o)
{
IntPtr h = IntPtr.Zero;
try
{
GCryptTransformInfo ti = (o as GCryptTransformInfo);
if(ti == null) { Debug.Assert(false); return; }
if(!GCryptInitCipher(ref h, ti)) return;
ulong r = 0;
IntPtr n16 = new IntPtr(16);
int tStart = Environment.TickCount;
while(true)
{
for(ulong j = 0; j < BenchStep; ++j)
{
if(NativeMethods.gcry_cipher_encrypt(h, ti.Data16, n16,
IntPtr.Zero, IntPtr.Zero) != 0)
{
Debug.Assert(false);
return;
}
}
r += BenchStep;
if(r < BenchStep) // Overflow check
{
r = ulong.MaxValue;
break;
}
uint tElapsed = (uint)(Environment.TickCount - tStart);
if(tElapsed > ti.TimeMs) break;
}
ti.Rounds = r;
ti.Success = true;
}
catch(Exception) { Debug.Assert(false); }
finally
{
try { if(h != IntPtr.Zero) NativeMethods.gcry_cipher_close(h); }
catch(Exception) { Debug.Assert(false); }
}
} */
// =============================================================
// Single-threaded implementation
private static bool GCryptInitCipher(ref IntPtr h, IntPtr pSeed32)
{
NativeMethods.gcry_cipher_open(ref h, NativeMethods.GCRY_CIPHER_AES256,
NativeMethods.GCRY_CIPHER_MODE_ECB, 0);
if(h == IntPtr.Zero) { Debug.Assert(false); return false; }
IntPtr n32 = new IntPtr(32);
if(NativeMethods.gcry_cipher_setkey(h, pSeed32, n32) != 0)
{
Debug.Assert(false);
return false;
}
return true;
}
private static bool GCryptBegin(byte[] pbData32, byte[] pbSeed32,
ref IntPtr h, ref IntPtr pData32, ref IntPtr pSeed32)
{
pData32 = Marshal.AllocCoTaskMem(32);
pSeed32 = Marshal.AllocCoTaskMem(32);
Marshal.Copy(pbData32, 0, pData32, 32);
Marshal.Copy(pbSeed32, 0, pSeed32, 32);
return GCryptInitCipher(ref h, pSeed32);
}
private static void GCryptEnd(IntPtr h, IntPtr pData32, IntPtr pSeed32)
{
NativeMethods.gcry_cipher_close(h);
Marshal.WriteInt64(pData32, 0);
Marshal.WriteInt64(pData32, 8, 0);
Marshal.WriteInt64(pData32, 16, 0);
Marshal.WriteInt64(pData32, 24, 0);
Marshal.FreeCoTaskMem(pData32);
Marshal.FreeCoTaskMem(pSeed32);
}
private static bool TransformKeyGCryptPriv(byte[] pbData32, byte[] pbSeed32,
ulong uRounds)
{
IntPtr h = IntPtr.Zero, pData32 = IntPtr.Zero, pSeed32 = IntPtr.Zero;
if(!GCryptBegin(pbData32, pbSeed32, ref h, ref pData32, ref pSeed32))
return false;
try
{
IntPtr n32 = new IntPtr(32);
for(ulong i = 0; i < uRounds; ++i)
{
if(NativeMethods.gcry_cipher_encrypt(h, pData32, n32,
IntPtr.Zero, IntPtr.Zero) != 0)
{
Debug.Assert(false);
return false;
}
}
Marshal.Copy(pData32, pbData32, 0, 32);
return true;
}
catch(Exception) { Debug.Assert(false); }
finally { GCryptEnd(h, pData32, pSeed32); }
return false;
}
private static bool TransformKeyBenchmarkGCryptPriv(uint uTimeMs, ref ulong uRounds)
{
byte[] pbData32 = new byte[32];
byte[] pbSeed32 = new byte[32];
IntPtr h = IntPtr.Zero, pData32 = IntPtr.Zero, pSeed32 = IntPtr.Zero;
if(!GCryptBegin(pbData32, pbSeed32, ref h, ref pData32, ref pSeed32))
return false;
uint uMaxMs = uTimeMs;
ulong uDiv = 1;
if(uMaxMs <= (uint.MaxValue >> 1)) { uMaxMs *= 2U; uDiv = 2; }
try
{
ulong r = 0;
IntPtr n32 = new IntPtr(32);
int tStart = Environment.TickCount;
while(true)
{
for(ulong j = 0; j < BenchStep; ++j)
{
if(NativeMethods.gcry_cipher_encrypt(h, pData32, n32,
IntPtr.Zero, IntPtr.Zero) != 0)
{
Debug.Assert(false);
return false;
}
}
r += BenchStep;
if(r < BenchStep) // Overflow check
{
r = ulong.MaxValue;
break;
}
uint tElapsed = (uint)(Environment.TickCount - tStart);
if(tElapsed > uMaxMs) break;
}
uRounds = r / uDiv;
return true;
}
catch(Exception) { Debug.Assert(false); }
finally { GCryptEnd(h, pData32, pSeed32); }
return false;
}
}
}

View File

@@ -0,0 +1,281 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2019 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
#if KeePassUAP
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Parameters;
#else
using System.Security.Cryptography;
#endif
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 static readonly string ParamRounds = "R"; // UInt64
public static readonly 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
{
#if !ModernKeePassLib
if(NativeLib.TransformKey256(pbNewKey, pbKeySeed32, uNumRounds))
return CryptoUtil.HashSha256(pbNewKey);
#endif
if(TransformKeyGCrypt(pbNewKey, pbKeySeed32, uNumRounds))
return CryptoUtil.HashSha256(pbNewKey);
if(TransformKeyManaged(pbNewKey, pbKeySeed32, uNumRounds))
return CryptoUtil.HashSha256(pbNewKey);
}
finally { MemUtil.ZeroByteArray(pbNewKey); }
return null;
}
public static bool TransformKeyManaged(byte[] pbNewKey32, byte[] pbKeySeed32,
ulong uNumRounds)
{
#if KeePassUAP
KeyParameter kp = new KeyParameter(pbKeySeed32);
AesEngine aes = new AesEngine();
aes.Init(true, kp);
for(ulong u = 0; u < uNumRounds; ++u)
{
aes.ProcessBlock(pbNewKey32, 0, pbNewKey32, 0);
aes.ProcessBlock(pbNewKey32, 16, pbNewKey32, 16);
}
aes.Reset();
#else
byte[] pbIV = new byte[16];
using(SymmetricAlgorithm a = CryptoUtil.CreateAes())
{
if(a.BlockSize != 128) // AES block size
{
Debug.Assert(false);
a.BlockSize = 128;
}
a.KeySize = 256;
a.Mode = CipherMode.ECB;
using(ICryptoTransform t = a.CreateEncryptor(pbKeySeed32, pbIV))
{
// !t.CanReuseTransform -- doesn't work with Mono
if((t == null) || (t.InputBlockSize != 16) ||
(t.OutputBlockSize != 16))
{
Debug.Assert(false);
return false;
}
for(ulong u = 0; u < uNumRounds; ++u)
{
t.TransformBlock(pbNewKey32, 0, 16, pbNewKey32, 0);
t.TransformBlock(pbNewKey32, 16, 16, pbNewKey32, 16);
}
}
}
#endif
return true;
}
public override KdfParameters GetBestParameters(uint uMilliseconds)
{
KdfParameters p = GetDefaultParameters();
ulong uRounds;
#if !ModernKeePassLib
// Try native method
if(NativeLib.TransformKeyBenchmark256(uMilliseconds, out uRounds))
{
p.SetUInt64(ParamRounds, uRounds);
return p;
}
#endif
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;
}
#if KeePassUAP
KeyParameter kp = new KeyParameter(pbKey);
AesEngine aes = new AesEngine();
aes.Init(true, kp);
#else
byte[] pbIV = new byte[16];
using(SymmetricAlgorithm a = CryptoUtil.CreateAes())
{
if(a.BlockSize != 128) // AES block size
{
Debug.Assert(false);
a.BlockSize = 128;
}
a.KeySize = 256;
a.Mode = CipherMode.ECB;
using(ICryptoTransform t = a.CreateEncryptor(pbKey, pbIV))
{
// !t.CanReuseTransform -- doesn't work with Mono
if((t == null) || (t.InputBlockSize != 16) ||
(t.OutputBlockSize != 16))
{
Debug.Assert(false);
p.SetUInt64(ParamRounds, PwDefs.DefaultKeyEncryptionRounds);
return p;
}
#endif
uRounds = 0;
int tStart = Environment.TickCount;
while(true)
{
for(ulong j = 0; j < BenchStep; ++j)
{
#if KeePassUAP
aes.ProcessBlock(pbNewKey, 0, pbNewKey, 0);
aes.ProcessBlock(pbNewKey, 16, pbNewKey, 16);
#else
t.TransformBlock(pbNewKey, 0, 16, pbNewKey, 0);
t.TransformBlock(pbNewKey, 16, 16, pbNewKey, 16);
#endif
}
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);
#if KeePassUAP
aes.Reset();
#else
}
}
#endif
return p;
}
}
}

View File

@@ -0,0 +1,637 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2019 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
// 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 System.Threading.Tasks;
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 ModernKeePassLib || 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);
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];
#if ModernKeePassLib || KeePassUAP
Array.Copy(vSrc, (int)uSrcOffset, vDst, (int)uDstOffset,
(int)NbBlockSizeInQW);
#else
Array.Copy(vSrc, (long)uSrcOffset, vDst, (long)uDstOffset,
(long)NbBlockSizeInQW);
#endif
}
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(MemUtil.EmptyByteArray, 0, 0);
Array.Copy(hOut.Hash, pbOut, cbOut);
if(cbOut < 64) hOut.Clear();
return;
}
h.Initialize();
h.TransformBlock(pbOutLen, 0, pbOutLen.Length, pbOutLen, 0);
h.TransformBlock(pbIn, 0, cbIn, pbIn, 0);
h.TransformFinalBlock(MemUtil.EmptyByteArray, 0, 0);
byte[] pbOutBuffer = new byte[64];
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 ModernKeePassLib
Task.Factory.StartNew(FillSegmentThr, ti);
//ThreadPool.RunAsync(a => FillSegmentThr(ti));
#else
if(!ThreadPool.QueueUserWorkItem(FillSegmentThr, ti))
{
Debug.Assert(false);
throw new OutOfMemoryException();
}
#endif
v[l] = ti;
}
for(int l = 0; l < np; ++l)
{
v[l].Finished.WaitOne();
v[l].Release();
}
}
}
}
private static void FillSegmentThr(object o)
{
Argon2ThreadInfo ti = (o as Argon2ThreadInfo);
if(ti == null) { Debug.Assert(false); return; }
try
{
Argon2Ctx ctx = ti.Context;
if(ctx == null) { Debug.Assert(false); return; }
Debug.Assert(ctx.Version >= MinVersion);
bool bCanXor = (ctx.Version >= 0x13U);
ulong uStart = 0;
if((ti.Pass == 0) && (ti.Slice == 0)) uStart = 2;
ulong uCur = (ti.Lane * ctx.LaneLength) + (ti.Slice *
ctx.SegmentLength) + uStart;
ulong uPrev = (((uCur % ctx.LaneLength) == 0) ?
(uCur + ctx.LaneLength - 1UL) : (uCur - 1UL));
ulong[] pbR = new ulong[NbBlockSizeInQW];
ulong[] pbTmp = new ulong[NbBlockSizeInQW];
for(ulong i = uStart; i < ctx.SegmentLength; ++i)
{
if((uCur % ctx.LaneLength) == 1)
uPrev = uCur - 1UL;
ulong uPseudoRand = ctx.Mem[uPrev * NbBlockSizeInQW];
ulong uRefLane = (uPseudoRand >> 32) % ctx.Lanes;
if((ti.Pass == 0) && (ti.Slice == 0))
uRefLane = ti.Lane;
ti.Index = i;
ulong uRefIndex = IndexAlpha(ctx, ti, (uint)uPseudoRand,
(uRefLane == ti.Lane));
ulong uRefBlockIndex = (ctx.LaneLength * uRefLane +
uRefIndex) * NbBlockSizeInQW;
ulong uCurBlockIndex = uCur * NbBlockSizeInQW;
FillBlock(ctx.Mem, uPrev * NbBlockSizeInQW, uRefBlockIndex,
uCurBlockIndex, ((ti.Pass != 0) && bCanXor), pbR, pbTmp);
++uCur;
++uPrev;
}
MemUtil.ZeroArray<ulong>(pbR);
MemUtil.ZeroArray<ulong>(pbTmp);
}
catch(Exception) { Debug.Assert(false); }
try { ti.Finished.Set(); }
catch(Exception) { Debug.Assert(false); }
}
#if ARGON2_B2ROUND_ARRAYS
private static void InitB2RoundIndexArrays()
{
int[][] vCols = g_vFBCols;
if(vCols == null)
{
vCols = new int[8][];
Debug.Assert(vCols.Length == 8);
int e = 0;
for(int i = 0; i < 8; ++i)
{
vCols[i] = new int[16];
for(int j = 0; j < 16; ++j)
{
vCols[i][j] = e;
++e;
}
}
g_vFBCols = vCols;
}
int[][] vRows = g_vFBRows;
if(vRows == null)
{
vRows = new int[8][];
for(int i = 0; i < 8; ++i)
{
vRows[i] = new int[16];
for(int j = 0; j < 16; ++j)
{
int jh = j / 2;
vRows[i][j] = (2 * i) + (16 * jh) + (j & 1);
}
}
g_vFBRows = vRows;
}
}
#endif
private static void FillBlock(ulong[] pMem, ulong uPrev, ulong uRef,
ulong uNext, bool bXor, ulong[] pbR, ulong[] pbTmp)
{
CopyBlock(pbR, 0, pMem, uRef);
XorBlock(pbR, 0, pMem, uPrev);
CopyBlock(pbTmp, 0, pbR, 0);
if(bXor) XorBlock(pbTmp, 0, pMem, uNext);
#if ARGON2_B2ROUND_ARRAYS
int[][] vCols = g_vFBCols;
int[][] vRows = g_vFBRows;
for(int i = 0; i < 8; ++i)
Blake2RoundNoMsg(pbR, vCols[i]);
for(int i = 0; i < 8; ++i)
Blake2RoundNoMsg(pbR, vRows[i]);
#else
for(int i = 0; i < (8 * 16); i += 16)
Blake2RoundNoMsgCols16i(pbR, i);
for(int i = 0; i < (8 * 2); i += 2)
Blake2RoundNoMsgRows2i(pbR, i);
#endif
CopyBlock(pMem, uNext, pbTmp, 0);
XorBlock(pMem, uNext, pbR, 0);
}
private static byte[] FinalHash(Argon2Ctx ctx, int cbOut, Blake2b h)
{
ulong[] pqBlockHash = new ulong[NbBlockSizeInQW];
CopyBlock(pqBlockHash, 0, ctx.Mem, (ctx.LaneLength - 1UL) *
NbBlockSizeInQW);
for(ulong l = 1; l < ctx.Lanes; ++l)
XorBlock(pqBlockHash, 0, ctx.Mem, (l * ctx.LaneLength +
ctx.LaneLength - 1UL) * NbBlockSizeInQW);
byte[] pbBlockHashBytes = new byte[NbBlockSize];
StoreBlock(pbBlockHashBytes, pqBlockHash);
byte[] pbOut = new byte[cbOut];
Blake2bLong(pbOut, cbOut, pbBlockHashBytes, (int)NbBlockSize, h);
MemUtil.ZeroArray<ulong>(pqBlockHash);
MemUtil.ZeroByteArray(pbBlockHashBytes);
return pbOut;
}
}
}

View File

@@ -0,0 +1,144 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2019 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
using System;
using System.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 static readonly string ParamSalt = "S"; // Byte[]
public static readonly string ParamParallelism = "P"; // UInt32
public static readonly string ParamMemory = "M"; // UInt64
public static readonly string ParamIterations = "I"; // UInt64
public static readonly string ParamVersion = "V"; // UInt32
public static readonly string ParamSecretKey = "K"; // Byte[]
public static readonly string ParamAssocData = "A"; // Byte[]
private const uint MinVersion = 0x10;
private const uint MaxVersion = 0x13;
private const int MinSalt = 8;
private const int MaxSalt = int.MaxValue; // .NET limit; 2^32 - 1 in spec
internal const ulong MinIterations = 1;
internal const ulong MaxIterations = uint.MaxValue;
internal const ulong MinMemory = 1024 * 8; // For parallelism = 1
// internal const ulong MaxMemory = (ulong)uint.MaxValue * 1024UL; // Spec
internal const ulong MaxMemory = int.MaxValue; // .NET limit
internal const uint MinParallelism = 1;
internal const uint MaxParallelism = (1 << 24) - 1;
internal const ulong DefaultIterations = 2;
internal const ulong DefaultMemory = 1024 * 1024; // 1 MB
internal const uint DefaultParallelism = 2;
public override PwUuid Uuid
{
get { return g_uuid; }
}
public override string Name
{
get { return "Argon2"; }
}
public Argon2Kdf()
{
}
public override KdfParameters GetDefaultParameters()
{
KdfParameters p = base.GetDefaultParameters();
p.SetUInt32(ParamVersion, MaxVersion);
p.SetUInt64(ParamIterations, DefaultIterations);
p.SetUInt64(ParamMemory, DefaultMemory);
p.SetUInt32(ParamParallelism, DefaultParallelism);
return p;
}
public override void Randomize(KdfParameters p)
{
if(p == null) { Debug.Assert(false); return; }
Debug.Assert(g_uuid.Equals(p.KdfUuid));
byte[] pb = CryptoRandom.Instance.GetRandomBytes(32);
p.SetByteArray(ParamSalt, pb);
}
public override byte[] Transform(byte[] pbMsg, KdfParameters p)
{
if(pbMsg == null) throw new ArgumentNullException("pbMsg");
if(p == null) throw new ArgumentNullException("p");
byte[] pbSalt = p.GetByteArray(ParamSalt);
if(pbSalt == null)
throw new ArgumentNullException("p.Salt");
if((pbSalt.Length < MinSalt) || (pbSalt.Length > MaxSalt))
throw new ArgumentOutOfRangeException("p.Salt");
uint uPar = p.GetUInt32(ParamParallelism, 0);
if((uPar < MinParallelism) || (uPar > MaxParallelism))
throw new ArgumentOutOfRangeException("p.Parallelism");
ulong uMem = p.GetUInt64(ParamMemory, 0);
if((uMem < MinMemory) || (uMem > MaxMemory))
throw new ArgumentOutOfRangeException("p.Memory");
ulong uIt = p.GetUInt64(ParamIterations, 0);
if((uIt < MinIterations) || (uIt > MaxIterations))
throw new ArgumentOutOfRangeException("p.Iterations");
uint v = p.GetUInt32(ParamVersion, 0);
if((v < MinVersion) || (v > MaxVersion))
throw new ArgumentOutOfRangeException("p.Version");
byte[] pbSecretKey = p.GetByteArray(ParamSecretKey);
byte[] pbAssocData = p.GetByteArray(ParamAssocData);
byte[] pbRet = Argon2d(pbMsg, pbSalt, uPar, uMem, uIt,
32, v, pbSecretKey, pbAssocData);
if(uMem > (100UL * 1024UL * 1024UL)) GC.Collect();
return pbRet;
}
public override KdfParameters GetBestParameters(uint uMilliseconds)
{
KdfParameters p = GetDefaultParameters();
Randomize(p);
MaximizeParamUInt64(p, ParamIterations, MinIterations,
MaxIterations, uMilliseconds, true);
return p;
}
}
}

View File

@@ -0,0 +1,142 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2019 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
namespace ModernKeePassLib.Cryptography.KeyDerivation
{
public abstract class KdfEngine
{
public abstract PwUuid Uuid
{
get;
}
public abstract string Name
{
get;
}
public virtual KdfParameters GetDefaultParameters()
{
return new KdfParameters(this.Uuid);
}
/// <summary>
/// Generate random seeds and store them in <paramref name="p" />.
/// </summary>
public virtual void Randomize(KdfParameters p)
{
Debug.Assert(p != null);
Debug.Assert(p.KdfUuid.Equals(this.Uuid));
}
public abstract byte[] Transform(byte[] pbMsg, KdfParameters p);
public virtual KdfParameters GetBestParameters(uint uMilliseconds)
{
throw new NotImplementedException();
}
protected void MaximizeParamUInt64(KdfParameters p, string strName,
ulong uMin, ulong uMax, uint uMilliseconds, bool bInterpSearch)
{
if(p == null) { Debug.Assert(false); return; }
if(string.IsNullOrEmpty(strName)) { Debug.Assert(false); return; }
if(uMin > uMax) { Debug.Assert(false); return; }
if(uMax > (ulong.MaxValue >> 1))
{
Debug.Assert(false);
uMax = ulong.MaxValue >> 1;
if(uMin > uMax) { p.SetUInt64(strName, uMin); return; }
}
byte[] pbMsg = new byte[32];
for(int i = 0; i < pbMsg.Length; ++i) pbMsg[i] = (byte)i;
ulong uLow = uMin;
ulong uHigh = uMin + 1UL;
long tLow = 0;
long tHigh = 0;
long tTarget = (long)uMilliseconds;
// Determine range
while(uHigh <= uMax)
{
p.SetUInt64(strName, uHigh);
// GC.Collect();
Stopwatch sw = Stopwatch.StartNew();
Transform(pbMsg, p);
sw.Stop();
tHigh = sw.ElapsedMilliseconds;
if(tHigh > tTarget) break;
uLow = uHigh;
tLow = tHigh;
uHigh <<= 1;
}
if(uHigh > uMax) { uHigh = uMax; tHigh = 0; }
if(uLow > uHigh) uLow = uHigh; // Skips to end
// Find optimal number of iterations
while((uHigh - uLow) >= 2UL)
{
ulong u = (uHigh + uLow) >> 1; // Binary search
// Interpolation search, if possible
if(bInterpSearch && (tLow > 0) && (tHigh > tTarget) &&
(tLow <= tTarget))
{
u = uLow + (((uHigh - uLow) * (ulong)(tTarget - tLow)) /
(ulong)(tHigh - tLow));
if((u >= uLow) && (u <= uHigh))
{
u = Math.Max(u, uLow + 1UL);
u = Math.Min(u, uHigh - 1UL);
}
else
{
Debug.Assert(false);
u = (uHigh + uLow) >> 1;
}
}
p.SetUInt64(strName, u);
// GC.Collect();
Stopwatch sw = Stopwatch.StartNew();
Transform(pbMsg, p);
sw.Stop();
long t = sw.ElapsedMilliseconds;
if(t == tTarget) { uLow = u; break; }
else if(t > tTarget) { uHigh = u; tHigh = t; }
else { uLow = u; tLow = t; }
}
p.SetUInt64(strName, uLow);
}
}
}

View File

@@ -0,0 +1,80 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2019 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;
using ModernKeePassLib.Collections;
using ModernKeePassLib.Utility;
namespace ModernKeePassLib.Cryptography.KeyDerivation
{
public sealed class KdfParameters : VariantDictionary
{
private const string ParamUuid = @"$UUID";
private readonly PwUuid m_puKdf;
public PwUuid KdfUuid
{
get { return m_puKdf; }
}
public KdfParameters(PwUuid puKdf)
{
if(puKdf == null) throw new ArgumentNullException("puKdf");
m_puKdf = puKdf;
SetByteArray(ParamUuid, puKdf.UuidBytes);
}
/// <summary>
/// Unsupported.
/// </summary>
public override object Clone()
{
throw new NotSupportedException();
}
public static byte[] SerializeExt(KdfParameters p)
{
return VariantDictionary.Serialize(p);
}
public static KdfParameters DeserializeExt(byte[] pb)
{
VariantDictionary d = VariantDictionary.Deserialize(pb);
if(d == null) { Debug.Assert(false); return null; }
byte[] pbUuid = d.GetByteArray(ParamUuid);
if((pbUuid == null) || (pbUuid.Length != (int)PwUuid.UuidSize))
{
Debug.Assert(false);
return null;
}
PwUuid pu = new PwUuid(pbUuid);
KdfParameters p = new KdfParameters(pu);
d.CopyTo(p);
return p;
}
}
}

View File

@@ -0,0 +1,96 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2019 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using ModernKeePassLib.Utility;
namespace ModernKeePassLib.Cryptography.KeyDerivation
{
public static class KdfPool
{
private static List<KdfEngine> g_l = new List<KdfEngine>();
public static IEnumerable<KdfEngine> Engines
{
get
{
EnsureInitialized();
return g_l;
}
}
private static void EnsureInitialized()
{
if(g_l.Count > 0) return;
g_l.Add(new AesKdf());
g_l.Add(new Argon2Kdf());
}
internal static KdfParameters GetDefaultParameters()
{
EnsureInitialized();
return g_l[0].GetDefaultParameters();
}
public static KdfEngine Get(PwUuid pu)
{
if(pu == null) { Debug.Assert(false); return null; }
EnsureInitialized();
foreach(KdfEngine kdf in g_l)
{
if(pu.Equals(kdf.Uuid)) return kdf;
}
return null;
}
public static KdfEngine Get(string strName)
{
if(string.IsNullOrEmpty(strName)) { Debug.Assert(false); return null; }
EnsureInitialized();
foreach(KdfEngine kdf in g_l)
{
if(strName.Equals(kdf.Name, StrUtil.CaseIgnoreCmp)) return kdf;
}
return null;
}
public static void Add(KdfEngine kdf)
{
if(kdf == null) { Debug.Assert(false); return; }
EnsureInitialized();
if(Get(kdf.Uuid) != null) { Debug.Assert(false); return; }
if(Get(kdf.Name) != null) { Debug.Assert(false); return; }
g_l.Add(kdf);
}
}
}

View File

@@ -0,0 +1,64 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2019 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using ModernKeePassLib.Security;
using ModernKeePassLib.Utility;
namespace ModernKeePassLib.Cryptography.PasswordGenerator
{
internal static class CharSetBasedGenerator
{
internal static PwgError Generate(out ProtectedString psOut,
PwProfile pwProfile, CryptoRandomStream crsRandomSource)
{
psOut = ProtectedString.Empty;
if(pwProfile.Length == 0) return PwgError.Success;
PwCharSet pcs = new PwCharSet(pwProfile.CharSet.ToString());
PwGenerator.PrepareCharSet(pcs, pwProfile);
char[] v = new char[pwProfile.Length];
try
{
for(int i = 0; i < v.Length; ++i)
{
char ch = PwGenerator.GenerateCharacter(pwProfile,
pcs, crsRandomSource);
if(ch == char.MinValue)
return PwgError.TooFewCharacters;
v[i] = ch;
}
byte[] pbUtf8 = StrUtil.Utf8.GetBytes(v);
psOut = new ProtectedString(true, pbUtf8);
MemUtil.ZeroByteArray(pbUtf8);
}
finally { MemUtil.ZeroArray<char>(v); }
return PwgError.Success;
}
}
}

View File

@@ -0,0 +1,66 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2019 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
using System;
using System.Collections.Generic;
using System.Text;
using ModernKeePassLib;
using ModernKeePassLib.Security;
namespace ModernKeePassLib.Cryptography.PasswordGenerator
{
public abstract class CustomPwGenerator
{
/// <summary>
/// Each custom password generation algorithm must have
/// its own unique UUID.
/// </summary>
public abstract PwUuid Uuid { get; }
/// <summary>
/// Displayable name of the password generation algorithm.
/// </summary>
public abstract string Name { get; }
public virtual bool SupportsOptions
{
get { return false; }
}
/// <summary>
/// Password generation function.
/// </summary>
/// <param name="prf">Password generation options chosen
/// by the user. This may be <c>null</c>, if the default
/// options should be used.</param>
/// <param name="crsRandomSource">Source that the algorithm
/// can use to generate random numbers.</param>
/// <returns>Generated password or <c>null</c> in case
/// of failure. If returning <c>null</c>, the caller assumes
/// that an error message has already been shown to the user.</returns>
public abstract ProtectedString Generate(PwProfile prf,
CryptoRandomStream crsRandomSource);
public virtual string GetOptions(string strCurrentOptions)
{
return string.Empty;
}
}
}

View File

@@ -0,0 +1,110 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2019 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
namespace ModernKeePassLib.Cryptography.PasswordGenerator
{
public sealed class CustomPwGeneratorPool : IEnumerable<CustomPwGenerator>
{
private List<CustomPwGenerator> m_vGens = new List<CustomPwGenerator>();
public int Count
{
get { return m_vGens.Count; }
}
public CustomPwGeneratorPool()
{
}
IEnumerator IEnumerable.GetEnumerator()
{
return m_vGens.GetEnumerator();
}
public IEnumerator<CustomPwGenerator> GetEnumerator()
{
return m_vGens.GetEnumerator();
}
public void Add(CustomPwGenerator pwg)
{
if(pwg == null) throw new ArgumentNullException("pwg");
PwUuid uuid = pwg.Uuid;
if(uuid == null) throw new ArgumentException();
int nIndex = FindIndex(uuid);
if(nIndex >= 0) m_vGens[nIndex] = pwg; // Replace
else m_vGens.Add(pwg);
}
public CustomPwGenerator Find(PwUuid uuid)
{
if(uuid == null) throw new ArgumentNullException("uuid");
foreach(CustomPwGenerator pwg in m_vGens)
{
if(uuid.Equals(pwg.Uuid)) return pwg;
}
return null;
}
public CustomPwGenerator Find(string strName)
{
if(strName == null) throw new ArgumentNullException("strName");
foreach(CustomPwGenerator pwg in m_vGens)
{
if(pwg.Name == strName) return pwg;
}
return null;
}
private int FindIndex(PwUuid uuid)
{
if(uuid == null) throw new ArgumentNullException("uuid");
for(int i = 0; i < m_vGens.Count; ++i)
{
if(uuid.Equals(m_vGens[i].Uuid)) return i;
}
return -1;
}
public bool Remove(PwUuid uuid)
{
if(uuid == null) throw new ArgumentNullException("uuid");
int nIndex = FindIndex(uuid);
if(nIndex < 0) return false;
m_vGens.RemoveAt(nIndex);
return true;
}
}
}

View File

@@ -0,0 +1,187 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2019 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using ModernKeePassLib.Security;
using ModernKeePassLib.Utility;
namespace ModernKeePassLib.Cryptography.PasswordGenerator
{
internal static class PatternBasedGenerator
{
internal static PwgError Generate(out ProtectedString psOut,
PwProfile pwProfile, CryptoRandomStream crsRandomSource)
{
psOut = ProtectedString.Empty;
LinkedList<char> llGenerated = new LinkedList<char>();
PwCharSet pcsCurrent = new PwCharSet();
PwCharSet pcsCustom = new PwCharSet();
PwCharSet pcsUsed = new PwCharSet();
bool bInCharSetDef = false;
string strPattern = ExpandPattern(pwProfile.Pattern);
if(strPattern.Length == 0) return PwgError.Success;
CharStream csStream = new CharStream(strPattern);
char ch = csStream.ReadChar();
while(ch != char.MinValue)
{
pcsCurrent.Clear();
bool bGenerateChar = false;
if(ch == '\\')
{
ch = csStream.ReadChar();
if(ch == char.MinValue) // Backslash at the end
{
llGenerated.AddLast('\\');
break;
}
if(bInCharSetDef) pcsCustom.Add(ch);
else
{
llGenerated.AddLast(ch);
pcsUsed.Add(ch);
}
}
else if(ch == '^')
{
ch = csStream.ReadChar();
if(ch == char.MinValue) // ^ at the end
{
llGenerated.AddLast('^');
break;
}
if(bInCharSetDef) pcsCustom.Remove(ch);
}
else if(ch == '[')
{
pcsCustom.Clear();
bInCharSetDef = true;
}
else if(ch == ']')
{
pcsCurrent.Add(pcsCustom.ToString());
bInCharSetDef = false;
bGenerateChar = true;
}
else if(bInCharSetDef)
{
if(pcsCustom.AddCharSet(ch) == false)
pcsCustom.Add(ch);
}
else if(pcsCurrent.AddCharSet(ch) == false)
{
llGenerated.AddLast(ch);
pcsUsed.Add(ch);
}
else bGenerateChar = true;
if(bGenerateChar)
{
PwGenerator.PrepareCharSet(pcsCurrent, pwProfile);
if(pwProfile.NoRepeatingCharacters)
pcsCurrent.Remove(pcsUsed.ToString());
char chGen = PwGenerator.GenerateCharacter(pwProfile,
pcsCurrent, crsRandomSource);
if(chGen == char.MinValue) return PwgError.TooFewCharacters;
llGenerated.AddLast(chGen);
pcsUsed.Add(chGen);
}
ch = csStream.ReadChar();
}
if(llGenerated.Count == 0) return PwgError.Success;
char[] v = new char[llGenerated.Count];
llGenerated.CopyTo(v, 0);
if(pwProfile.PatternPermutePassword)
PwGenerator.Shuffle(v, crsRandomSource);
byte[] pbUtf8 = StrUtil.Utf8.GetBytes(v);
psOut = new ProtectedString(true, pbUtf8);
MemUtil.ZeroByteArray(pbUtf8);
MemUtil.ZeroArray<char>(v);
llGenerated.Clear();
return PwgError.Success;
}
private static string ExpandPattern(string strPattern)
{
if(strPattern == null) { Debug.Assert(false); return string.Empty; }
string str = strPattern;
while(true)
{
int nOpen = FindFirstUnescapedChar(str, '{');
int nClose = FindFirstUnescapedChar(str, '}');
if((nOpen >= 0) && (nOpen < nClose))
{
string strCount = str.Substring(nOpen + 1, nClose - nOpen - 1);
str = str.Remove(nOpen, nClose - nOpen + 1);
uint uRepeat;
if(StrUtil.TryParseUInt(strCount, out uRepeat) && (nOpen >= 1))
{
if(uRepeat == 0)
str = str.Remove(nOpen - 1, 1);
else
str = str.Insert(nOpen, new string(str[nOpen - 1], (int)uRepeat - 1));
}
}
else break;
}
return str;
}
private static int FindFirstUnescapedChar(string str, char ch)
{
for(int i = 0; i < str.Length; ++i)
{
char chCur = str[i];
if(chCur == '\\') ++i; // Next is escaped, skip it
else if(chCur == ch) return i;
}
return -1;
}
}
}

View File

@@ -0,0 +1,351 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2019 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
namespace ModernKeePassLib.Cryptography.PasswordGenerator
{
public sealed class PwCharSet
{
public static readonly string UpperCase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
public static readonly string LowerCase = "abcdefghijklmnopqrstuvwxyz";
public static readonly string Digits = "0123456789";
public static readonly string UpperConsonants = "BCDFGHJKLMNPQRSTVWXYZ";
public static readonly string LowerConsonants = "bcdfghjklmnpqrstvwxyz";
public static readonly string UpperVowels = "AEIOU";
public static readonly string LowerVowels = "aeiou";
public static readonly string Punctuation = @",.;:";
public static readonly string Brackets = @"[]{}()<>";
public static readonly string PrintableAsciiSpecial = "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~";
public static readonly string UpperHex = "0123456789ABCDEF";
public static readonly string LowerHex = "0123456789abcdef";
public static readonly string Invalid = "\t\r\n";
public static readonly string LookAlike = @"O0l1I|";
internal static readonly string MenuAccels = PwCharSet.LowerCase + PwCharSet.Digits;
private const int CharTabSize = 0x10000 / 8;
private List<char> m_vChars = new List<char>();
private byte[] m_vTab = new byte[CharTabSize];
private static string m_strHighAnsi = null;
public static string HighAnsiChars
{
get
{
if(m_strHighAnsi == null) { new PwCharSet(); } // Create string
Debug.Assert(m_strHighAnsi != null);
return m_strHighAnsi;
}
}
private static string m_strSpecial = null;
public static string SpecialChars
{
get
{
if(m_strSpecial == null) { new PwCharSet(); } // Create string
Debug.Assert(m_strSpecial != null);
return m_strSpecial;
}
}
/// <summary>
/// Create a new, empty character set collection object.
/// </summary>
public PwCharSet()
{
Initialize(true);
}
public PwCharSet(string strCharSet)
{
Initialize(true);
Add(strCharSet);
}
private PwCharSet(bool bFullInitialize)
{
Initialize(bFullInitialize);
}
private void Initialize(bool bFullInitialize)
{
Clear();
if(!bFullInitialize) return;
if(m_strHighAnsi == null)
{
StringBuilder sbHighAnsi = new StringBuilder();
// [U+0080, U+009F] are C1 control characters,
// U+00A0 is non-breaking space
for(char ch = '\u00A1'; ch <= '\u00AC'; ++ch)
sbHighAnsi.Append(ch);
// U+00AD is soft hyphen (format character)
for(char ch = '\u00AE'; ch < '\u00FF'; ++ch)
sbHighAnsi.Append(ch);
sbHighAnsi.Append('\u00FF');
m_strHighAnsi = sbHighAnsi.ToString();
}
if(m_strSpecial == null)
{
PwCharSet pcs = new PwCharSet(false);
pcs.AddRange('!', '/');
pcs.AddRange(':', '@');
pcs.AddRange('[', '`');
pcs.Add(@"|~");
pcs.Remove(@"-_ ");
pcs.Remove(PwCharSet.Brackets);
m_strSpecial = pcs.ToString();
}
}
/// <summary>
/// Number of characters in this set.
/// </summary>
public uint Size
{
get { return (uint)m_vChars.Count; }
}
/// <summary>
/// Get a character of the set using an index.
/// </summary>
/// <param name="uPos">Index of the character to get.</param>
/// <returns>Character at the specified position. If the index is invalid,
/// an <c>ArgumentOutOfRangeException</c> is thrown.</returns>
public char this[uint uPos]
{
get
{
if(uPos >= (uint)m_vChars.Count)
throw new ArgumentOutOfRangeException("uPos");
return m_vChars[(int)uPos];
}
}
/// <summary>
/// Remove all characters from this set.
/// </summary>
public void Clear()
{
m_vChars.Clear();
Array.Clear(m_vTab, 0, m_vTab.Length);
}
public bool Contains(char ch)
{
return (((m_vTab[ch / 8] >> (ch % 8)) & 1) != char.MinValue);
}
public bool Contains(string strCharacters)
{
Debug.Assert(strCharacters != null);
if(strCharacters == null) throw new ArgumentNullException("strCharacters");
foreach(char ch in strCharacters)
{
if(!Contains(ch)) return false;
}
return true;
}
/// <summary>
/// Add characters to the set.
/// </summary>
/// <param name="ch">Character to add.</param>
public void Add(char ch)
{
if(ch == char.MinValue) { Debug.Assert(false); return; }
if(!Contains(ch))
{
m_vChars.Add(ch);
m_vTab[ch / 8] |= (byte)(1 << (ch % 8));
}
}
/// <summary>
/// Add characters to the set.
/// </summary>
/// <param name="strCharSet">String containing characters to add.</param>
public void Add(string strCharSet)
{
Debug.Assert(strCharSet != null);
if(strCharSet == null) throw new ArgumentNullException("strCharSet");
m_vChars.Capacity = m_vChars.Count + strCharSet.Length;
foreach(char ch in strCharSet)
Add(ch);
}
public void Add(string strCharSet1, string strCharSet2)
{
Add(strCharSet1);
Add(strCharSet2);
}
public void Add(string strCharSet1, string strCharSet2, string strCharSet3)
{
Add(strCharSet1);
Add(strCharSet2);
Add(strCharSet3);
}
public void AddRange(char chMin, char chMax)
{
m_vChars.Capacity = m_vChars.Count + (chMax - chMin) + 1;
for(char ch = chMin; ch < chMax; ++ch)
Add(ch);
Add(chMax);
}
public bool AddCharSet(char chCharSetIdentifier)
{
bool bResult = true;
switch(chCharSetIdentifier)
{
case 'a': Add(PwCharSet.LowerCase, PwCharSet.Digits); break;
case 'A': Add(PwCharSet.LowerCase, PwCharSet.UpperCase,
PwCharSet.Digits); break;
case 'U': Add(PwCharSet.UpperCase, PwCharSet.Digits); break;
case 'c': Add(PwCharSet.LowerConsonants); break;
case 'C': Add(PwCharSet.LowerConsonants,
PwCharSet.UpperConsonants); break;
case 'z': Add(PwCharSet.UpperConsonants); break;
case 'd': Add(PwCharSet.Digits); break; // Digit
case 'h': Add(PwCharSet.LowerHex); break;
case 'H': Add(PwCharSet.UpperHex); break;
case 'l': Add(PwCharSet.LowerCase); break;
case 'L': Add(PwCharSet.LowerCase, PwCharSet.UpperCase); break;
case 'u': Add(PwCharSet.UpperCase); break;
case 'p': Add(PwCharSet.Punctuation); break;
case 'b': Add(PwCharSet.Brackets); break;
case 's': Add(PwCharSet.PrintableAsciiSpecial); break;
case 'S': Add(PwCharSet.UpperCase, PwCharSet.LowerCase);
Add(PwCharSet.Digits, PwCharSet.PrintableAsciiSpecial); break;
case 'v': Add(PwCharSet.LowerVowels); break;
case 'V': Add(PwCharSet.LowerVowels, PwCharSet.UpperVowels); break;
case 'Z': Add(PwCharSet.UpperVowels); break;
case 'x': Add(m_strHighAnsi); break;
default: bResult = false; break;
}
return bResult;
}
public bool Remove(char ch)
{
m_vTab[ch / 8] &= (byte)(~(1 << (ch % 8)));
return m_vChars.Remove(ch);
}
public bool Remove(string strCharacters)
{
Debug.Assert(strCharacters != null);
if(strCharacters == null) throw new ArgumentNullException("strCharacters");
bool bResult = true;
foreach(char ch in strCharacters)
{
if(!Remove(ch)) bResult = false;
}
return bResult;
}
public bool RemoveIfAllExist(string strCharacters)
{
Debug.Assert(strCharacters != null);
if(strCharacters == null) throw new ArgumentNullException("strCharacters");
if(!Contains(strCharacters))
return false;
return Remove(strCharacters);
}
/// <summary>
/// Convert the character set to a string containing all its characters.
/// </summary>
/// <returns>String containing all character set characters.</returns>
public override string ToString()
{
StringBuilder sb = new StringBuilder();
foreach(char ch in m_vChars)
sb.Append(ch);
return sb.ToString();
}
public string PackAndRemoveCharRanges()
{
StringBuilder sb = new StringBuilder();
sb.Append(RemoveIfAllExist(PwCharSet.UpperCase) ? 'U' : '_');
sb.Append(RemoveIfAllExist(PwCharSet.LowerCase) ? 'L' : '_');
sb.Append(RemoveIfAllExist(PwCharSet.Digits) ? 'D' : '_');
sb.Append(RemoveIfAllExist(m_strSpecial) ? 'S' : '_');
sb.Append(RemoveIfAllExist(PwCharSet.Punctuation) ? 'P' : '_');
sb.Append(RemoveIfAllExist(@"-") ? 'm' : '_');
sb.Append(RemoveIfAllExist(@"_") ? 'u' : '_');
sb.Append(RemoveIfAllExist(@" ") ? 's' : '_');
sb.Append(RemoveIfAllExist(PwCharSet.Brackets) ? 'B' : '_');
sb.Append(RemoveIfAllExist(m_strHighAnsi) ? 'H' : '_');
return sb.ToString();
}
public void UnpackCharRanges(string strRanges)
{
if(strRanges == null) { Debug.Assert(false); return; }
if(strRanges.Length < 10) { Debug.Assert(false); return; }
if(strRanges[0] != '_') Add(PwCharSet.UpperCase);
if(strRanges[1] != '_') Add(PwCharSet.LowerCase);
if(strRanges[2] != '_') Add(PwCharSet.Digits);
if(strRanges[3] != '_') Add(m_strSpecial);
if(strRanges[4] != '_') Add(PwCharSet.Punctuation);
if(strRanges[5] != '_') Add('-');
if(strRanges[6] != '_') Add('_');
if(strRanges[7] != '_') Add(' ');
if(strRanges[8] != '_') Add(PwCharSet.Brackets);
if(strRanges[9] != '_') Add(m_strHighAnsi);
}
}
}

View File

@@ -0,0 +1,163 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2019 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
#if !KeePassUAP
using System.Security.Cryptography;
#endif
using ModernKeePassLib.Security;
using ModernKeePassLib.Utility;
namespace ModernKeePassLib.Cryptography.PasswordGenerator
{
public enum PwgError
{
Success = 0,
Unknown = 1,
TooFewCharacters = 2,
UnknownAlgorithm = 3
}
/// <summary>
/// Utility functions for generating random passwords.
/// </summary>
public static class PwGenerator
{
public static PwgError Generate(out ProtectedString psOut,
PwProfile pwProfile, byte[] pbUserEntropy,
CustomPwGeneratorPool pwAlgorithmPool)
{
Debug.Assert(pwProfile != null);
if(pwProfile == null) throw new ArgumentNullException("pwProfile");
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; }
}
finally
{
if(crs != null) crs.Dispose();
if(pbKey != null) MemUtil.ZeroByteArray(pbKey);
}
return e;
}
private static CryptoRandomStream CreateRandomStream(byte[] pbAdditionalEntropy,
out byte[] pbKey)
{
pbKey = CryptoRandom.Instance.GetRandomBytes(128);
// Mix in additional entropy
Debug.Assert(pbKey.Length >= 64);
if((pbAdditionalEntropy != null) && (pbAdditionalEntropy.Length > 0))
{
using(SHA512Managed h = new SHA512Managed())
{
byte[] pbHash = h.ComputeHash(pbAdditionalEntropy);
MemUtil.XorArray(pbHash, 0, pbKey, 0, pbHash.Length);
}
}
return new CryptoRandomStream(CrsAlgorithm.ChaCha20, pbKey);
}
internal static char GenerateCharacter(PwProfile pwProfile,
PwCharSet pwCharSet, CryptoRandomStream crsRandomSource)
{
if(pwCharSet.Size == 0) return char.MinValue;
ulong uIndex = crsRandomSource.GetRandomUInt64();
uIndex %= (ulong)pwCharSet.Size;
char ch = pwCharSet[(uint)uIndex];
if(pwProfile.NoRepeatingCharacters)
pwCharSet.Remove(ch);
return ch;
}
internal static void PrepareCharSet(PwCharSet pwCharSet, PwProfile pwProfile)
{
pwCharSet.Remove(PwCharSet.Invalid);
if(pwProfile.ExcludeLookAlike) pwCharSet.Remove(PwCharSet.LookAlike);
if(pwProfile.ExcludeCharacters.Length > 0)
pwCharSet.Remove(pwProfile.ExcludeCharacters);
}
internal static void Shuffle(char[] v, CryptoRandomStream crsRandomSource)
{
if(v == null) { Debug.Assert(false); return; }
if(crsRandomSource == null) { Debug.Assert(false); return; }
for(int i = v.Length - 1; i >= 1; --i)
{
ulong r = crsRandomSource.GetRandomUInt64();
int j = (int)(r % (ulong)(i + 1));
char t = v[i];
v[i] = v[j];
v[j] = t;
}
}
private static PwgError GenerateCustom(out ProtectedString psOut,
PwProfile pwProfile, CryptoRandomStream crs,
CustomPwGeneratorPool pwAlgorithmPool)
{
psOut = ProtectedString.Empty;
Debug.Assert(pwProfile.GeneratorType == PasswordGeneratorType.Custom);
if(pwAlgorithmPool == null) return PwgError.UnknownAlgorithm;
string strID = pwProfile.CustomAlgorithmUuid;
if(string.IsNullOrEmpty(strID)) return PwgError.UnknownAlgorithm;
byte[] pbUuid = Convert.FromBase64String(strID);
PwUuid uuid = new PwUuid(pbUuid);
CustomPwGenerator pwg = pwAlgorithmPool.Find(uuid);
if(pwg == null) { Debug.Assert(false); return PwgError.UnknownAlgorithm; }
ProtectedString pwd = pwg.Generate(pwProfile.CloneDeep(), crs);
if(pwd == null) return PwgError.Unknown;
psOut = pwd;
return PwgError.Success;
}
}
}

View File

@@ -0,0 +1,276 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2019 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
using System;
using System.Collections.Generic;
using System.Text;
using System.Xml.Serialization;
using System.ComponentModel;
using System.Diagnostics;
using ModernKeePassLib.Interfaces;
using ModernKeePassLib.Security;
using ModernKeePassLib.Utility;
namespace ModernKeePassLib.Cryptography.PasswordGenerator
{
/// <summary>
/// Type of the password generator. Different types like generators
/// based on given patterns, based on character sets, etc. are
/// available.
/// </summary>
public enum PasswordGeneratorType
{
/// <summary>
/// Generator based on character spaces/sets, i.e. groups
/// of characters like lower-case, upper-case or numeric characters.
/// </summary>
CharSet = 0,
/// <summary>
/// Password generation based on a pattern. The user has provided
/// a pattern, which describes how the generated password has to
/// look like.
/// </summary>
Pattern = 1,
Custom = 2
}
public sealed class PwProfile : IDeepCloneable<PwProfile>
{
private string m_strName = string.Empty;
[DefaultValue("")]
public string Name
{
get { return m_strName; }
set { m_strName = value; }
}
private PasswordGeneratorType m_type = PasswordGeneratorType.CharSet;
public PasswordGeneratorType GeneratorType
{
get { return m_type; }
set { m_type = value; }
}
private bool m_bUserEntropy = false;
[DefaultValue(false)]
public bool CollectUserEntropy
{
get { return m_bUserEntropy; }
set { m_bUserEntropy = value; }
}
private uint m_uLength = 20;
public uint Length
{
get { return m_uLength; }
set { m_uLength = value; }
}
private PwCharSet m_pwCharSet = new PwCharSet(PwCharSet.UpperCase +
PwCharSet.LowerCase + PwCharSet.Digits);
[XmlIgnore]
public PwCharSet CharSet
{
get { return m_pwCharSet; }
set
{
if(value == null) throw new ArgumentNullException("value");
m_pwCharSet = value;
}
}
private string m_strCharSetRanges = string.Empty;
[DefaultValue("")]
public string CharSetRanges
{
get { this.UpdateCharSet(true); return m_strCharSetRanges; }
set
{
if(value == null) throw new ArgumentNullException("value");
m_strCharSetRanges = value;
this.UpdateCharSet(false);
}
}
private string m_strCharSetAdditional = string.Empty;
[DefaultValue("")]
public string CharSetAdditional
{
get { this.UpdateCharSet(true); return m_strCharSetAdditional; }
set
{
if(value == null) throw new ArgumentNullException("value");
m_strCharSetAdditional = value;
this.UpdateCharSet(false);
}
}
private string m_strPattern = string.Empty;
[DefaultValue("")]
public string Pattern
{
get { return m_strPattern; }
set { m_strPattern = value; }
}
private bool m_bPatternPermute = false;
[DefaultValue(false)]
public bool PatternPermutePassword
{
get { return m_bPatternPermute; }
set { m_bPatternPermute = value; }
}
private bool m_bNoLookAlike = false;
[DefaultValue(false)]
public bool ExcludeLookAlike
{
get { return m_bNoLookAlike; }
set { m_bNoLookAlike = value; }
}
private bool m_bNoRepeat = false;
[DefaultValue(false)]
public bool NoRepeatingCharacters
{
get { return m_bNoRepeat; }
set { m_bNoRepeat = value; }
}
private string m_strExclude = string.Empty;
[DefaultValue("")]
public string ExcludeCharacters
{
get { return m_strExclude; }
set
{
if(value == null) throw new ArgumentNullException("value");
m_strExclude = value;
}
}
private string m_strCustomID = string.Empty;
[DefaultValue("")]
public string CustomAlgorithmUuid
{
get { return m_strCustomID; }
set
{
if(value == null) throw new ArgumentNullException("value");
m_strCustomID = value;
}
}
private string m_strCustomOpt = string.Empty;
[DefaultValue("")]
public string CustomAlgorithmOptions
{
get { return m_strCustomOpt; }
set
{
if(value == null) throw new ArgumentNullException("value");
m_strCustomOpt = value;
}
}
public PwProfile()
{
}
public PwProfile CloneDeep()
{
PwProfile p = new PwProfile();
p.m_strName = m_strName;
p.m_type = m_type;
p.m_bUserEntropy = m_bUserEntropy;
p.m_uLength = m_uLength;
p.m_pwCharSet = new PwCharSet(m_pwCharSet.ToString());
p.m_strCharSetRanges = m_strCharSetRanges;
p.m_strCharSetAdditional = m_strCharSetAdditional;
p.m_strPattern = m_strPattern;
p.m_bPatternPermute = m_bPatternPermute;
p.m_bNoLookAlike = m_bNoLookAlike;
p.m_bNoRepeat = m_bNoRepeat;
p.m_strExclude = m_strExclude;
p.m_strCustomID = m_strCustomID;
p.m_strCustomOpt = m_strCustomOpt;
return p;
}
private void UpdateCharSet(bool bSetXml)
{
if(bSetXml)
{
PwCharSet pcs = new PwCharSet(m_pwCharSet.ToString());
m_strCharSetRanges = pcs.PackAndRemoveCharRanges();
m_strCharSetAdditional = pcs.ToString();
}
else
{
PwCharSet pcs = new PwCharSet(m_strCharSetAdditional);
pcs.UnpackCharRanges(m_strCharSetRanges);
m_pwCharSet = pcs;
}
}
public static PwProfile DeriveFromPassword(ProtectedString psPassword)
{
PwProfile pp = new PwProfile();
Debug.Assert(psPassword != null); if(psPassword == null) return pp;
char[] vChars = psPassword.ReadChars();
pp.GeneratorType = PasswordGeneratorType.CharSet;
pp.Length = (uint)vChars.Length;
PwCharSet pcs = pp.CharSet;
pcs.Clear();
foreach(char ch in vChars)
{
if((ch >= 'A') && (ch <= 'Z')) pcs.Add(PwCharSet.UpperCase);
else if((ch >= 'a') && (ch <= 'z')) pcs.Add(PwCharSet.LowerCase);
else if((ch >= '0') && (ch <= '9')) pcs.Add(PwCharSet.Digits);
else if(PwCharSet.SpecialChars.IndexOf(ch) >= 0)
pcs.Add(PwCharSet.SpecialChars);
else if(ch == ' ') pcs.Add(' ');
else if(ch == '-') pcs.Add('-');
else if(ch == '_') pcs.Add('_');
else if(PwCharSet.Brackets.IndexOf(ch) >= 0)
pcs.Add(PwCharSet.Brackets);
else if(PwCharSet.HighAnsiChars.IndexOf(ch) >= 0)
pcs.Add(PwCharSet.HighAnsiChars);
else pcs.Add(ch);
}
MemUtil.ZeroArray<char>(vChars);
return pp;
}
public bool HasSecurityReducingOption()
{
return (m_bNoLookAlike || m_bNoRepeat || (m_strExclude.Length > 0));
}
}
}

View File

@@ -0,0 +1,133 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2019 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using ModernKeePassLib.Utility;
namespace ModernKeePassLib.Cryptography
{
public static class PopularPasswords
{
private static Dictionary<int, Dictionary<char[], bool>> m_dicts =
new Dictionary<int, Dictionary<char[], bool>>();
internal static int MaxLength
{
get
{
Debug.Assert(m_dicts.Count > 0); // Should be initialized
int iMaxLen = 0;
foreach(int iLen in m_dicts.Keys)
{
if(iLen > iMaxLen) iMaxLen = iLen;
}
return iMaxLen;
}
}
internal static bool ContainsLength(int nLength)
{
Dictionary<char[], bool> dDummy;
return m_dicts.TryGetValue(nLength, out dDummy);
}
public static bool IsPopularPassword(char[] vPassword)
{
ulong uDummy;
return IsPopularPassword(vPassword, out uDummy);
}
public static bool IsPopularPassword(char[] vPassword, out ulong uDictSize)
{
if(vPassword == null) throw new ArgumentNullException("vPassword");
if(vPassword.Length == 0) { uDictSize = 0; return false; }
#if DEBUG
Array.ForEach(vPassword, ch => Debug.Assert(ch == char.ToLower(ch)));
#endif
try { return IsPopularPasswordPriv(vPassword, out uDictSize); }
catch(Exception) { Debug.Assert(false); }
uDictSize = 0;
return false;
}
private static bool IsPopularPasswordPriv(char[] vPassword, out ulong uDictSize)
{
Debug.Assert(m_dicts.Count > 0); // Should be initialized with data
Dictionary<char[], bool> d;
if(!m_dicts.TryGetValue(vPassword.Length, out d))
{
uDictSize = 0;
return false;
}
uDictSize = (ulong)d.Count;
return d.ContainsKey(vPassword);
}
public static void Add(byte[] pbData, bool bGZipped)
{
try
{
if(bGZipped)
pbData = MemUtil.Decompress(pbData);
string strData = StrUtil.Utf8.GetString(pbData, 0, pbData.Length);
if(string.IsNullOrEmpty(strData)) { Debug.Assert(false); return; }
StringBuilder sb = new StringBuilder();
for(int i = 0; i <= strData.Length; ++i)
{
char ch = ((i == strData.Length) ? ' ' : strData[i]);
if(char.IsWhiteSpace(ch))
{
int cc = sb.Length;
if(cc > 0)
{
char[] vWord = new char[cc];
sb.CopyTo(0, vWord, 0, cc);
Dictionary<char[], bool> d;
if(!m_dicts.TryGetValue(cc, out d))
{
d = new Dictionary<char[], bool>(MemUtil.ArrayHelperExOfChar);
m_dicts[cc] = d;
}
d[vWord] = true;
sb.Remove(0, cc);
}
}
else sb.Append(char.ToLower(ch));
}
}
catch(Exception) { Debug.Assert(false); }
}
}
}

View File

@@ -0,0 +1,19 @@
using System;
using System.Security.Cryptography;
using ModernKeePassLib.Native;
namespace ModernKeePassLib.Cryptography
{
public static class ProtectedData
{
public static byte[] Unprotect(byte[] pbEnc, byte[] mPbOptEnt, DataProtectionScope currentUser)
{
throw new NotImplementedException();
}
public static byte[] Protect(byte[] pbPlain, byte[] mPbOptEnt, DataProtectionScope currentUser)
{
throw new NotImplementedException();
}
}
}

View File

@@ -0,0 +1,779 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2019 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using ModernKeePassLib.Cryptography.PasswordGenerator;
using ModernKeePassLib.Utility;
namespace ModernKeePassLib.Cryptography
{
/// <summary>
/// A class that offers static functions to estimate the quality of
/// passwords.
/// </summary>
public static class QualityEstimation
{
private static class PatternID
{
internal const char LowerAlpha = 'L';
internal const char UpperAlpha = 'U';
internal const char Digit = 'D';
internal const char Special = 'S';
internal const char High = 'H';
internal const char Other = 'X';
internal const char Dictionary = 'W';
internal const char Repetition = 'R';
internal const char Number = 'N';
internal const char DiffSeq = 'C';
internal const string All = "LUDSHXWRNC";
}
// private static class CharDistrib
// {
// public static readonly ulong[] LowerAlpha = new ulong[26] {
// 884, 211, 262, 249, 722, 98, 172, 234, 556, 124, 201, 447, 321,
// 483, 518, 167, 18, 458, 416, 344, 231, 105, 80, 48, 238, 76
// };
// public static readonly ulong[] UpperAlpha = new ulong[26] {
// 605, 188, 209, 200, 460, 81, 130, 163, 357, 122, 144, 332, 260,
// 317, 330, 132, 18, 320, 315, 250, 137, 76, 60, 36, 161, 54
// };
// public static readonly ulong[] Digit = new ulong[10] {
// 574, 673, 524, 377, 339, 336, 312, 310, 357, 386
// };
// }
private sealed class QeCharType
{
private readonly char m_chTypeID;
public char TypeID { get { return m_chTypeID; } }
private readonly string m_strAlph;
public string Alphabet { get { return m_strAlph; } }
private readonly int m_nChars;
public int CharCount { get { return m_nChars; } }
private readonly char m_chFirst;
private readonly char m_chLast;
private readonly double m_dblCharSize;
public double CharSize { get { return m_dblCharSize; } }
public QeCharType(char chTypeID, string strAlphabet, bool bIsConsecutive)
{
if(strAlphabet == null) throw new ArgumentNullException();
if(strAlphabet.Length == 0) throw new ArgumentException();
m_chTypeID = chTypeID;
m_strAlph = strAlphabet;
m_nChars = m_strAlph.Length;
m_chFirst = (bIsConsecutive ? m_strAlph[0] : char.MinValue);
m_chLast = (bIsConsecutive ? m_strAlph[m_nChars - 1] : char.MinValue);
m_dblCharSize = Log2(m_nChars);
Debug.Assert(((int)(m_chLast - m_chFirst) == (m_nChars - 1)) ||
!bIsConsecutive);
}
public QeCharType(char chTypeID, int nChars) // Catch-none set
{
if(nChars <= 0) throw new ArgumentOutOfRangeException();
m_chTypeID = chTypeID;
m_strAlph = string.Empty;
m_nChars = nChars;
m_chFirst = char.MinValue;
m_chLast = char.MinValue;
m_dblCharSize = Log2(m_nChars);
}
public bool Contains(char ch)
{
if(m_chLast != char.MinValue)
return ((ch >= m_chFirst) && (ch <= m_chLast));
Debug.Assert(m_strAlph.Length > 0); // Don't call for catch-none set
return (m_strAlph.IndexOf(ch) >= 0);
}
}
private sealed class EntropyEncoder
{
private readonly string m_strAlph;
private Dictionary<char, ulong> m_dHisto = new Dictionary<char, ulong>();
private readonly ulong m_uBaseWeight;
private readonly ulong m_uCharWeight;
private readonly ulong m_uOccExclThreshold;
public EntropyEncoder(string strAlphabet, ulong uBaseWeight,
ulong uCharWeight, ulong uOccExclThreshold)
{
if(strAlphabet == null) throw new ArgumentNullException();
if(strAlphabet.Length == 0) throw new ArgumentException();
m_strAlph = strAlphabet;
m_uBaseWeight = uBaseWeight;
m_uCharWeight = uCharWeight;
m_uOccExclThreshold = uOccExclThreshold;
#if DEBUG
Dictionary<char, bool> d = new Dictionary<char, bool>();
foreach(char ch in m_strAlph) { d[ch] = true; }
Debug.Assert(d.Count == m_strAlph.Length); // No duplicates
#endif
}
public void Reset()
{
m_dHisto.Clear();
}
public void Write(char ch)
{
Debug.Assert(m_strAlph.IndexOf(ch) >= 0);
ulong uOcc;
m_dHisto.TryGetValue(ch, out uOcc);
Debug.Assert(m_dHisto.ContainsKey(ch) || (uOcc == 0));
m_dHisto[ch] = uOcc + 1;
}
public double GetOutputSize()
{
ulong uTotalWeight = m_uBaseWeight * (ulong)m_strAlph.Length;
foreach(ulong u in m_dHisto.Values)
{
Debug.Assert(u >= 1);
if(u > m_uOccExclThreshold)
uTotalWeight += (u - m_uOccExclThreshold) * m_uCharWeight;
}
double dSize = 0.0, dTotalWeight = (double)uTotalWeight;
foreach(ulong u in m_dHisto.Values)
{
ulong uWeight = m_uBaseWeight;
if(u > m_uOccExclThreshold)
uWeight += (u - m_uOccExclThreshold) * m_uCharWeight;
dSize -= (double)u * Log2((double)uWeight / dTotalWeight);
}
return dSize;
}
}
private sealed class MultiEntropyEncoder
{
private Dictionary<char, EntropyEncoder> m_dEncs =
new Dictionary<char, EntropyEncoder>();
public MultiEntropyEncoder()
{
}
public void AddEncoder(char chTypeID, EntropyEncoder ec)
{
if(ec == null) { Debug.Assert(false); return; }
Debug.Assert(!m_dEncs.ContainsKey(chTypeID));
m_dEncs[chTypeID] = ec;
}
public void Reset()
{
foreach(EntropyEncoder ec in m_dEncs.Values) { ec.Reset(); }
}
public bool Write(char chTypeID, char chData)
{
EntropyEncoder ec;
if(!m_dEncs.TryGetValue(chTypeID, out ec))
return false;
ec.Write(chData);
return true;
}
public double GetOutputSize()
{
double d = 0.0;
foreach(EntropyEncoder ec in m_dEncs.Values)
{
d += ec.GetOutputSize();
}
return d;
}
}
private sealed class QePatternInstance
{
private readonly int m_iPos;
public int Position { get { return m_iPos; } }
private readonly int m_nLen;
public int Length { get { return m_nLen; } }
private readonly char m_chPatternID;
public char PatternID { get { return m_chPatternID; } }
private readonly double m_dblCost;
public double Cost { get { return m_dblCost; } }
private readonly QeCharType m_ctSingle;
public QeCharType SingleCharType { get { return m_ctSingle; } }
public QePatternInstance(int iPosition, int nLength, char chPatternID,
double dblCost)
{
m_iPos = iPosition;
m_nLen = nLength;
m_chPatternID = chPatternID;
m_dblCost = dblCost;
m_ctSingle = null;
}
public QePatternInstance(int iPosition, int nLength, QeCharType ctSingle)
{
m_iPos = iPosition;
m_nLen = nLength;
m_chPatternID = ctSingle.TypeID;
m_dblCost = ctSingle.CharSize;
m_ctSingle = ctSingle;
}
}
private sealed class QePathState
{
public readonly int Position;
public readonly List<QePatternInstance> Path;
public QePathState(int iPosition, List<QePatternInstance> lPath)
{
this.Position = iPosition;
this.Path = lPath;
}
}
private static readonly object m_objSyncInit = new object();
private static List<QeCharType> m_lCharTypes = null;
private static void EnsureInitialized()
{
lock(m_objSyncInit)
{
if(m_lCharTypes == null)
{
string strSpecial = PwCharSet.PrintableAsciiSpecial;
if(strSpecial.IndexOf(' ') >= 0) { Debug.Assert(false); }
else strSpecial = strSpecial + " ";
int nSp = strSpecial.Length;
int nHi = PwCharSet.HighAnsiChars.Length;
m_lCharTypes = new List<QeCharType>();
m_lCharTypes.Add(new QeCharType(PatternID.LowerAlpha,
PwCharSet.LowerCase, true));
m_lCharTypes.Add(new QeCharType(PatternID.UpperAlpha,
PwCharSet.UpperCase, true));
m_lCharTypes.Add(new QeCharType(PatternID.Digit,
PwCharSet.Digits, true));
m_lCharTypes.Add(new QeCharType(PatternID.Special,
strSpecial, false));
m_lCharTypes.Add(new QeCharType(PatternID.High,
PwCharSet.HighAnsiChars, false));
m_lCharTypes.Add(new QeCharType(PatternID.Other,
0x10000 - (2 * 26) - 10 - nSp - nHi));
}
}
}
/// <summary>
/// Estimate the quality of a password.
/// </summary>
/// <param name="vPassword">Password to check.</param>
/// <returns>Estimated bit-strength of the password.</returns>
public static uint EstimatePasswordBits(char[] vPassword)
{
if(vPassword == null) { Debug.Assert(false); return 0; }
if(vPassword.Length == 0) return 0;
EnsureInitialized();
int n = vPassword.Length;
List<QePatternInstance>[] vPatterns = new List<QePatternInstance>[n];
for(int i = 0; i < n; ++i)
{
vPatterns[i] = new List<QePatternInstance>();
QePatternInstance piChar = new QePatternInstance(i, 1,
GetCharType(vPassword[i]));
vPatterns[i].Add(piChar);
}
FindRepetitions(vPassword, vPatterns);
FindNumbers(vPassword, vPatterns);
FindDiffSeqs(vPassword, vPatterns);
FindPopularPasswords(vPassword, vPatterns);
// Encoders must not be static, because the entropy estimation
// may run concurrently in multiple threads and the encoders are
// not read-only
EntropyEncoder ecPattern = new EntropyEncoder(PatternID.All, 0, 1, 0);
MultiEntropyEncoder mcData = new MultiEntropyEncoder();
for(int i = 0; i < (m_lCharTypes.Count - 1); ++i)
{
// Let m be the alphabet size. In order to ensure that two same
// characters cost at least as much as a single character, for
// the probability p and weight w of the character it must hold:
// -log(1/m) >= -2*log(p)
// <=> log(1/m) <= log(p^2) <=> 1/m <= p^2 <=> p >= sqrt(1/m);
// sqrt(1/m) = (1+w)/(m+w)
// <=> m+w = (1+w)*sqrt(m) <=> m+w = sqrt(m) + w*sqrt(m)
// <=> w*(1-sqrt(m)) = sqrt(m) - m <=> w = (sqrt(m)-m)/(1-sqrt(m))
// <=> w = (sqrt(m)-m)*(1+sqrt(m))/(1-m)
// <=> w = (sqrt(m)-m+m-m*sqrt(m))/(1-m) <=> w = sqrt(m)
ulong uw = (ulong)Math.Sqrt((double)m_lCharTypes[i].CharCount);
mcData.AddEncoder(m_lCharTypes[i].TypeID, new EntropyEncoder(
m_lCharTypes[i].Alphabet, 1, uw, 1));
}
double dblMinCost = (double)int.MaxValue;
int tStart = Environment.TickCount;
Stack<QePathState> sRec = new Stack<QePathState>();
sRec.Push(new QePathState(0, new List<QePatternInstance>()));
while(sRec.Count > 0)
{
int tDiff = Environment.TickCount - tStart;
if(tDiff > 500) break;
QePathState s = sRec.Pop();
if(s.Position >= n)
{
Debug.Assert(s.Position == n);
double dblCost = ComputePathCost(s.Path, vPassword,
ecPattern, mcData);
if(dblCost < dblMinCost) dblMinCost = dblCost;
}
else
{
List<QePatternInstance> lSubs = vPatterns[s.Position];
for(int i = lSubs.Count - 1; i >= 0; --i)
{
QePatternInstance pi = lSubs[i];
Debug.Assert(pi.Position == s.Position);
Debug.Assert(pi.Length >= 1);
List<QePatternInstance> lNewPath =
new List<QePatternInstance>(s.Path.Count + 1);
lNewPath.AddRange(s.Path);
lNewPath.Add(pi);
Debug.Assert(lNewPath.Capacity == (s.Path.Count + 1));
QePathState sNew = new QePathState(s.Position +
pi.Length, lNewPath);
sRec.Push(sNew);
}
}
}
return (uint)Math.Ceiling(dblMinCost);
}
/// <summary>
/// Estimate the quality of a password.
/// </summary>
/// <param name="pbUnprotectedUtf8">Password to check, UTF-8 encoded.</param>
/// <returns>Estimated bit-strength of the password.</returns>
public static uint EstimatePasswordBits(byte[] pbUnprotectedUtf8)
{
if(pbUnprotectedUtf8 == null) { Debug.Assert(false); return 0; }
char[] v = StrUtil.Utf8.GetChars(pbUnprotectedUtf8);
uint r;
try { r = EstimatePasswordBits(v); }
finally { MemUtil.ZeroArray<char>(v); }
return r;
}
private static QeCharType GetCharType(char ch)
{
int nTypes = m_lCharTypes.Count;
Debug.Assert((nTypes > 0) && (m_lCharTypes[nTypes - 1].CharCount > 256));
for(int i = 0; i < (nTypes - 1); ++i)
{
if(m_lCharTypes[i].Contains(ch))
return m_lCharTypes[i];
}
return m_lCharTypes[nTypes - 1];
}
private static double ComputePathCost(List<QePatternInstance> l,
char[] vPassword, EntropyEncoder ecPattern, MultiEntropyEncoder mcData)
{
ecPattern.Reset();
for(int i = 0; i < l.Count; ++i)
ecPattern.Write(l[i].PatternID);
double dblPatternCost = ecPattern.GetOutputSize();
mcData.Reset();
double dblDataCost = 0.0;
foreach(QePatternInstance pi in l)
{
QeCharType tChar = pi.SingleCharType;
if(tChar != null)
{
char ch = vPassword[pi.Position];
if(!mcData.Write(tChar.TypeID, ch))
dblDataCost += pi.Cost;
}
else dblDataCost += pi.Cost;
}
dblDataCost += mcData.GetOutputSize();
return (dblPatternCost + dblDataCost);
}
private static void FindPopularPasswords(char[] vPassword,
List<QePatternInstance>[] vPatterns)
{
int n = vPassword.Length;
char[] vLower = new char[n];
char[] vLeet = new char[n];
for(int i = 0; i < n; ++i)
{
char ch = vPassword[i];
vLower[i] = char.ToLower(ch);
vLeet[i] = char.ToLower(DecodeLeetChar(ch));
}
char chErased = default(char); // The value that Array.Clear uses
Debug.Assert(chErased == char.MinValue);
int nMaxLen = Math.Min(n, PopularPasswords.MaxLength);
for(int nSubLen = nMaxLen; nSubLen >= 3; --nSubLen)
{
if(!PopularPasswords.ContainsLength(nSubLen)) continue;
char[] vSub = new char[nSubLen];
for(int i = 0; i <= (n - nSubLen); ++i)
{
if(Array.IndexOf<char>(vLower, chErased, i, nSubLen) >= 0)
continue;
Array.Copy(vLower, i, vSub, 0, nSubLen);
if(!EvalAddPopularPasswordPattern(vPatterns, vPassword,
i, vSub, 0.0))
{
Array.Copy(vLeet, i, vSub, 0, nSubLen);
if(EvalAddPopularPasswordPattern(vPatterns, vPassword,
i, vSub, 1.5))
{
Array.Clear(vLower, i, nSubLen); // Not vLeet
Debug.Assert(vLower[i] == chErased);
}
}
else
{
Array.Clear(vLower, i, nSubLen);
Debug.Assert(vLower[i] == chErased);
}
}
MemUtil.ZeroArray<char>(vSub);
}
MemUtil.ZeroArray<char>(vLower);
MemUtil.ZeroArray<char>(vLeet);
}
private static bool EvalAddPopularPasswordPattern(List<QePatternInstance>[] vPatterns,
char[] vPassword, int i, char[] vSub, double dblCostPerMod)
{
ulong uDictSize;
if(!PopularPasswords.IsPopularPassword(vSub, out uDictSize))
return false;
int n = vSub.Length;
int d = HammingDist(vSub, 0, vPassword, i, n);
double dblCost = Log2((double)uDictSize);
// dblCost += log2(n binom d)
int k = Math.Min(d, n - d);
for(int j = n; j > (n - k); --j)
dblCost += Log2(j);
for(int j = k; j >= 2; --j)
dblCost -= Log2(j);
dblCost += dblCostPerMod * (double)d;
vPatterns[i].Add(new QePatternInstance(i, n, PatternID.Dictionary,
dblCost));
return true;
}
private static char DecodeLeetChar(char chLeet)
{
if((chLeet >= '\u00C0') && (chLeet <= '\u00C6')) return 'a';
if((chLeet >= '\u00C8') && (chLeet <= '\u00CB')) return 'e';
if((chLeet >= '\u00CC') && (chLeet <= '\u00CF')) return 'i';
if((chLeet >= '\u00D2') && (chLeet <= '\u00D6')) return 'o';
if((chLeet >= '\u00D9') && (chLeet <= '\u00DC')) return 'u';
if((chLeet >= '\u00E0') && (chLeet <= '\u00E6')) return 'a';
if((chLeet >= '\u00E8') && (chLeet <= '\u00EB')) return 'e';
if((chLeet >= '\u00EC') && (chLeet <= '\u00EF')) return 'i';
if((chLeet >= '\u00F2') && (chLeet <= '\u00F6')) return 'o';
if((chLeet >= '\u00F9') && (chLeet <= '\u00FC')) return 'u';
char ch;
switch(chLeet)
{
case '4':
case '@':
case '?':
case '^':
case '\u00AA': ch = 'a'; break;
case '8':
case '\u00DF': ch = 'b'; break;
case '(':
case '{':
case '[':
case '<':
case '\u00A2':
case '\u00A9':
case '\u00C7':
case '\u00E7': ch = 'c'; break;
case '\u00D0':
case '\u00F0': ch = 'd'; break;
case '3':
case '\u20AC':
case '&':
case '\u00A3': ch = 'e'; break;
case '6':
case '9': ch = 'g'; break;
case '#': ch = 'h'; break;
case '1':
case '!':
case '|':
case '\u00A1':
case '\u00A6': ch = 'i'; break;
case '\u00D1':
case '\u00F1': ch = 'n'; break;
case '0':
case '*':
case '\u00A4': // Currency
case '\u00B0': // Degree
case '\u00D8':
case '\u00F8': ch = 'o'; break;
case '\u00AE': ch = 'r'; break;
case '$':
case '5':
case '\u00A7': ch = 's'; break;
case '+':
case '7': ch = 't'; break;
case '\u00B5': ch = 'u'; break;
case '%':
case '\u00D7': ch = 'x'; break;
case '\u00A5':
case '\u00DD':
case '\u00FD':
case '\u00FF': ch = 'y'; break;
case '2': ch = 'z'; break;
default: ch = chLeet; break;
}
return ch;
}
private static int HammingDist(char[] v1, int iOffset1,
char[] v2, int iOffset2, int nLength)
{
int nDist = 0;
for(int i = 0; i < nLength; ++i)
{
if(v1[iOffset1 + i] != v2[iOffset2 + i]) ++nDist;
}
return nDist;
}
private static void FindRepetitions(char[] vPassword,
List<QePatternInstance>[] vPatterns)
{
int n = vPassword.Length;
char[] v = new char[n];
Array.Copy(vPassword, v, n);
char chErased = char.MaxValue;
for(int m = (n / 2); m >= 3; --m)
{
for(int x1 = 0; x1 <= (n - (2 * m)); ++x1)
{
bool bFoundRep = false;
for(int x2 = (x1 + m); x2 <= (n - m); ++x2)
{
if(PartsEqual(v, x1, x2, m))
{
double dblCost = Log2(x1 + 1) + Log2(m);
vPatterns[x2].Add(new QePatternInstance(x2, m,
PatternID.Repetition, dblCost));
ErasePart(v, x2, m, ref chErased);
bFoundRep = true;
}
}
if(bFoundRep) ErasePart(v, x1, m, ref chErased);
}
}
MemUtil.ZeroArray<char>(v);
}
private static bool PartsEqual(char[] v, int x1, int x2, int nLength)
{
for(int i = 0; i < nLength; ++i)
{
if(v[x1 + i] != v[x2 + i]) return false;
}
return true;
}
private static void ErasePart(char[] v, int i, int n, ref char chErased)
{
for(int j = 0; j < n; ++j)
{
v[i + j] = chErased;
--chErased;
}
}
private static void FindNumbers(char[] vPassword,
List<QePatternInstance>[] vPatterns)
{
int n = vPassword.Length;
StringBuilder sb = new StringBuilder();
for(int i = 0; i < n; ++i)
{
char ch = vPassword[i];
if((ch >= '0') && (ch <= '9')) sb.Append(ch);
else
{
AddNumberPattern(vPatterns, sb, i - sb.Length);
sb.Remove(0, sb.Length);
}
}
AddNumberPattern(vPatterns, sb, n - sb.Length);
}
private static void AddNumberPattern(List<QePatternInstance>[] vPatterns,
StringBuilder sb, int i)
{
if(sb.Length <= 2) return;
string strNumber = sb.ToString();
int nZeros = 0;
for(int j = 0; j < strNumber.Length; ++j)
{
if(strNumber[j] != '0') break;
++nZeros;
}
double dblCost = Log2(nZeros + 1);
if(nZeros < strNumber.Length)
{
string strNonZero = strNumber.Substring(nZeros);
#if KeePassLibSD
try { dblCost += Log2(double.Parse(strNonZero)); }
catch(Exception) { Debug.Assert(false); return; }
#else
double d;
if(double.TryParse(strNonZero, out d))
dblCost += Log2(d);
else { Debug.Assert(false); return; }
#endif
}
vPatterns[i].Add(new QePatternInstance(i, strNumber.Length,
PatternID.Number, dblCost));
}
private static void FindDiffSeqs(char[] vPassword,
List<QePatternInstance>[] vPatterns)
{
int n = vPassword.Length;
int d = int.MaxValue, p = 0;
for(int i = 1; i <= n; ++i)
{
int dCur = ((i == n) ? int.MinValue :
((int)vPassword[i] - (int)vPassword[i - 1]));
if(dCur != d)
{
if((i - p) >= 3) // At least 3 chars involved
{
QeCharType ct = GetCharType(vPassword[p]);
double dblCost = ct.CharSize + Log2(i - p - 1);
vPatterns[p].Add(new QePatternInstance(p,
i - p, PatternID.DiffSeq, dblCost));
}
d = dCur;
p = i - 1;
}
}
}
private static double Log2(double dblValue)
{
#if KeePassLibSD
return (Math.Log(dblValue) / Math.Log(2.0));
#else
return Math.Log(dblValue, 2.0);
#endif
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,66 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2019 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
using System;
namespace ModernKeePassLib.Delegates
{
/// <summary>
/// Function definition of a method that performs an action on a group.
/// When traversing the internal tree, this function will be invoked
/// for all visited groups.
/// </summary>
/// <param name="pg">Currently visited group.</param>
/// <returns>You must return <c>true</c> if you want to continue the
/// traversal. If you want to immediately stop the whole traversal,
/// return <c>false</c>.</returns>
public delegate bool GroupHandler(PwGroup pg);
/// <summary>
/// Function definition of a method that performs an action on an entry.
/// When traversing the internal tree, this function will be invoked
/// for all visited entries.
/// </summary>
/// <param name="pe">Currently visited entry.</param>
/// <returns>You must return <c>true</c> if you want to continue the
/// traversal. If you want to immediately stop the whole traversal,
/// return <c>false</c>.</returns>
public delegate bool EntryHandler(PwEntry pe);
public delegate void VoidDelegate();
public delegate string StrPwEntryDelegate(string str, PwEntry pe);
// Action<...> with 0 or >= 2 parameters has been introduced in .NET 3.5
public delegate void GAction();
public delegate void GAction<T>(T o);
public delegate void GAction<T1, T2>(T1 o1, T2 o2);
public delegate void GAction<T1, T2, T3>(T1 o1, T2 o2, T3 o3);
public delegate void GAction<T1, T2, T3, T4>(T1 o1, T2 o2, T3 o3, T4 o4);
public delegate void GAction<T1, T2, T3, T4, T5>(T1 o1, T2 o2, T3 o3, T4 o4, T5 o5);
public delegate void GAction<T1, T2, T3, T4, T5, T6>(T1 o1, T2 o2, T3 o3, T4 o4, T5 o5, T6 o6);
// Func<...> has been introduced in .NET 3.5
public delegate TResult GFunc<TResult>();
public delegate TResult GFunc<T, TResult>(T o);
public delegate TResult GFunc<T1, T2, TResult>(T1 o1, T2 o2);
public delegate TResult GFunc<T1, T2, T3, TResult>(T1 o1, T2 o2, T3 o3);
public delegate TResult GFunc<T1, T2, T3, T4, TResult>(T1 o1, T2 o2, T3 o3, T4 o4);
public delegate TResult GFunc<T1, T2, T3, T4, T5, TResult>(T1 o1, T2 o2, T3 o3, T4 o4, T5 o5);
public delegate TResult GFunc<T1, T2, T3, T4, T5, T6, TResult>(T1 o1, T2 o2, T3 o3, T4 o4, T5 o5, T6 o6);
}

View File

@@ -0,0 +1,37 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2019 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
using System;
using System.Collections.Generic;
namespace ModernKeePassLib.Interfaces
{
/// <summary>
/// Interface for objects that are deeply cloneable.
/// </summary>
/// <typeparam name="T">Reference type.</typeparam>
public interface IDeepCloneable<T> where T : class
{
/// <summary>
/// Deeply clone the object.
/// </summary>
/// <returns>Cloned object.</returns>
T CloneDeep();
}
}

View File

@@ -0,0 +1,105 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2019 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
using System;
using System.Collections.Generic;
namespace ModernKeePassLib.Interfaces
{
/// <summary>
/// Status message types.
/// </summary>
public enum LogStatusType
{
/// <summary>
/// Default type: simple information type.
/// </summary>
Info = 0,
/// <summary>
/// Warning message.
/// </summary>
Warning,
/// <summary>
/// Error message.
/// </summary>
Error,
/// <summary>
/// Additional information. Depends on lines above.
/// </summary>
AdditionalInfo
}
/// <summary>
/// Status logging interface.
/// </summary>
public interface IStatusLogger
{
/// <summary>
/// Function which needs to be called when logging is started.
/// </summary>
/// <param name="strOperation">This string should roughly describe
/// the operation, of which the status is logged.</param>
/// <param name="bWriteOperationToLog">Specifies whether the
/// operation is written to the log or not.</param>
void StartLogging(string strOperation, bool bWriteOperationToLog);
/// <summary>
/// Function which needs to be called when logging is ended
/// (i.e. when no more messages will be logged and when the
/// percent value won't change any more).
/// </summary>
void EndLogging();
/// <summary>
/// Set the current progress in percent.
/// </summary>
/// <param name="uPercent">Percent of work finished.</param>
/// <returns>Returns <c>true</c> if the caller should continue
/// the current work.</returns>
bool SetProgress(uint uPercent);
/// <summary>
/// Set the current status text.
/// </summary>
/// <param name="strNewText">Status text.</param>
/// <param name="lsType">Type of the message.</param>
/// <returns>Returns <c>true</c> if the caller should continue
/// the current work.</returns>
bool SetText(string strNewText, LogStatusType lsType);
/// <summary>
/// Check if the user cancelled the current work.
/// </summary>
/// <returns>Returns <c>true</c> if the caller should continue
/// the current work.</returns>
bool ContinueWork();
}
public sealed class NullStatusLogger : IStatusLogger
{
public void StartLogging(string strOperation, bool bWriteOperationToLog) { }
public void EndLogging() { }
public bool SetProgress(uint uPercent) { return true; }
public bool SetText(string strNewText, LogStatusType lsType) { return true; }
public bool ContinueWork() { return true; }
}
}

View File

@@ -0,0 +1,37 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2019 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
using System;
namespace ModernKeePassLib.Interfaces
{
public interface IStructureItem : ITimeLogger // Provides LocationChanged
{
PwUuid Uuid
{
get;
set;
}
PwGroup ParentGroup
{
get;
}
}
}

View File

@@ -0,0 +1,105 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2019 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
using System;
namespace ModernKeePassLib.Interfaces
{
/// <summary>
/// Interface for objects that support various times (creation time, last
/// access time, last modification time and expiry time). Offers
/// several helper functions (for example a function to touch the current
/// object).
/// </summary>
public interface ITimeLogger
{
/// <summary>
/// The date/time when the object was created.
/// </summary>
DateTime CreationTime
{
get;
set;
}
/// <summary>
/// The date/time when the object was last modified.
/// </summary>
DateTime LastModificationTime
{
get;
set;
}
/// <summary>
/// The date/time when the object was last accessed.
/// </summary>
DateTime LastAccessTime
{
get;
set;
}
/// <summary>
/// The date/time when the object expires.
/// </summary>
DateTime ExpiryTime
{
get;
set;
}
/// <summary>
/// Flag that determines if the object does expire.
/// </summary>
bool Expires
{
get;
set;
}
/// <summary>
/// Get or set the usage count of the object. To increase the usage
/// count by one, use the <c>Touch</c> function.
/// </summary>
ulong UsageCount
{
get;
set;
}
/// <summary>
/// The date/time when the location of the object was last changed.
/// </summary>
DateTime LocationChanged
{
get;
set;
}
/// <summary>
/// Touch the object. This function updates the internal last access
/// time. If the <paramref name="bModified" /> parameter is <c>true</c>,
/// the last modification time gets updated, too. Each time you call
/// <c>Touch</c>, the usage count of the object is increased by one.
/// </summary>
/// <param name="bModified">Update last modification time.</param>
void Touch(bool bModified);
}
}

View File

@@ -0,0 +1,37 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2019 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
using System;
using System.Collections.Generic;
using System.Text;
namespace ModernKeePassLib.Interfaces
{
public interface IUIOperations
{
/// <summary>
/// Let the user interface save the current database.
/// </summary>
/// <param name="bForceSave">If <c>true</c>, the UI will not ask for
/// whether to synchronize or overwrite, it'll simply overwrite the
/// file.</param>
/// <returns>Returns <c>true</c> if the file has been saved.</returns>
bool UIFileSave(bool bForceSave);
}
}

View File

@@ -0,0 +1,33 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2019 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Xml;
namespace ModernKeePassLib.Interfaces
{
public interface IXmlSerializerEx
{
void Serialize(XmlWriter xmlWriter, object o);
object Deserialize(Stream s);
}
}

View File

@@ -0,0 +1,295 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2019 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using ModernKeePassLib.Cryptography;
using ModernKeePassLib.Cryptography.KeyDerivation;
using ModernKeePassLib.Resources;
using ModernKeePassLib.Security;
using ModernKeePassLib.Utility;
namespace ModernKeePassLib.Keys
{
/// <summary>
/// Represents a key. A key can be build up using several user key data sources
/// like a password, a key file, the currently logged on user credentials,
/// the current computer ID, etc.
/// </summary>
public sealed class CompositeKey
{
private List<IUserKey> m_vUserKeys = new List<IUserKey>();
/// <summary>
/// List of all user keys contained in the current composite key.
/// </summary>
public IEnumerable<IUserKey> UserKeys
{
get { return m_vUserKeys; }
}
public uint UserKeyCount
{
get { return (uint)m_vUserKeys.Count; }
}
/// <summary>
/// Construct a new, empty key object.
/// </summary>
public CompositeKey()
{
}
// /// <summary>
// /// Deconstructor, clears up the key.
// /// </summary>
// ~CompositeKey()
// {
// Clear();
// }
// /// <summary>
// /// Clears the key. This function also erases all previously stored
// /// user key data objects.
// /// </summary>
// public void Clear()
// {
// foreach(IUserKey pKey in m_vUserKeys)
// pKey.Clear();
// m_vUserKeys.Clear();
// }
/// <summary>
/// Add a user key.
/// </summary>
/// <param name="pKey">User key to add.</param>
public void AddUserKey(IUserKey pKey)
{
Debug.Assert(pKey != null); if(pKey == null) throw new ArgumentNullException("pKey");
m_vUserKeys.Add(pKey);
}
/// <summary>
/// Remove a user key.
/// </summary>
/// <param name="pKey">User key to remove.</param>
/// <returns>Returns <c>true</c> if the key was removed successfully.</returns>
public bool RemoveUserKey(IUserKey pKey)
{
Debug.Assert(pKey != null); if(pKey == null) throw new ArgumentNullException("pKey");
Debug.Assert(m_vUserKeys.IndexOf(pKey) >= 0);
return m_vUserKeys.Remove(pKey);
}
/// <summary>
/// Test whether the composite key contains a specific type of
/// user keys (password, key file, ...). If at least one user
/// key of that type is present, the function returns <c>true</c>.
/// </summary>
/// <param name="tUserKeyType">User key type.</param>
/// <returns>Returns <c>true</c>, if the composite key contains
/// a user key of the specified type.</returns>
public bool ContainsType(Type tUserKeyType)
{
Debug.Assert(tUserKeyType != null);
if(tUserKeyType == null) throw new ArgumentNullException("tUserKeyType");
foreach(IUserKey pKey in m_vUserKeys)
{
if(pKey == null) { Debug.Assert(false); continue; }
#if KeePassUAP
if(pKey.GetType() == tUserKeyType)
return true;
#else
if(tUserKeyType.IsInstanceOfType(pKey))
return true;
#endif
}
return false;
}
/// <summary>
/// Get the first user key of a specified type.
/// </summary>
/// <param name="tUserKeyType">Type of the user key to get.</param>
/// <returns>Returns the first user key of the specified type
/// or <c>null</c> if no key of that type is found.</returns>
public IUserKey GetUserKey(Type tUserKeyType)
{
Debug.Assert(tUserKeyType != null);
if(tUserKeyType == null) throw new ArgumentNullException("tUserKeyType");
foreach(IUserKey pKey in m_vUserKeys)
{
if(pKey == null) { Debug.Assert(false); continue; }
#if KeePassUAP
if(pKey.GetType() == tUserKeyType)
return pKey;
#else
if(tUserKeyType.IsInstanceOfType(pKey))
return pKey;
#endif
}
return null;
}
/// <summary>
/// Creates the composite key from the supplied user key sources (password,
/// key file, user account, computer ID, etc.).
/// </summary>
private byte[] CreateRawCompositeKey32()
{
ValidateUserKeys();
// Concatenate user key data
List<byte[]> lData = new List<byte[]>();
int cbData = 0;
foreach(IUserKey pKey in m_vUserKeys)
{
ProtectedBinary b = pKey.KeyData;
if(b != null)
{
byte[] pbKeyData = b.ReadData();
lData.Add(pbKeyData);
cbData += pbKeyData.Length;
}
}
byte[] pbAllData = new byte[cbData];
int p = 0;
foreach(byte[] pbData in lData)
{
Array.Copy(pbData, 0, pbAllData, p, pbData.Length);
p += pbData.Length;
MemUtil.ZeroByteArray(pbData);
}
Debug.Assert(p == cbData);
byte[] pbHash = CryptoUtil.HashSha256(pbAllData);
MemUtil.ZeroByteArray(pbAllData);
return pbHash;
}
public bool EqualsValue(CompositeKey ckOther)
{
if(ckOther == null) throw new ArgumentNullException("ckOther");
byte[] pbThis = CreateRawCompositeKey32();
byte[] pbOther = ckOther.CreateRawCompositeKey32();
bool bResult = MemUtil.ArraysEqual(pbThis, pbOther);
MemUtil.ZeroByteArray(pbOther);
MemUtil.ZeroByteArray(pbThis);
return bResult;
}
[Obsolete]
public ProtectedBinary GenerateKey32(byte[] pbKeySeed32, ulong uNumRounds)
{
Debug.Assert(pbKeySeed32 != null);
if(pbKeySeed32 == null) throw new ArgumentNullException("pbKeySeed32");
Debug.Assert(pbKeySeed32.Length == 32);
if(pbKeySeed32.Length != 32) throw new ArgumentException("pbKeySeed32");
AesKdf kdf = new AesKdf();
KdfParameters p = kdf.GetDefaultParameters();
p.SetUInt64(AesKdf.ParamRounds, uNumRounds);
p.SetByteArray(AesKdf.ParamSeed, pbKeySeed32);
return GenerateKey32(p);
}
/// <summary>
/// Generate a 32-byte (256-bit) key from the composite key.
/// </summary>
public ProtectedBinary GenerateKey32(KdfParameters p)
{
if(p == null) { Debug.Assert(false); throw new ArgumentNullException("p"); }
byte[] pbRaw32 = CreateRawCompositeKey32();
if((pbRaw32 == null) || (pbRaw32.Length != 32))
{ Debug.Assert(false); return null; }
KdfEngine kdf = KdfPool.Get(p.KdfUuid);
if(kdf == null) // CryptographicExceptions are translated to "file corrupted"
throw new Exception(KLRes.UnknownKdf + MessageService.NewParagraph +
KLRes.FileNewVerOrPlgReq + MessageService.NewParagraph +
"UUID: " + p.KdfUuid.ToHexString() + ".");
byte[] pbTrf32 = kdf.Transform(pbRaw32, p);
if(pbTrf32 == null) { Debug.Assert(false); return null; }
if(pbTrf32.Length != 32)
{
Debug.Assert(false);
pbTrf32 = CryptoUtil.HashSha256(pbTrf32);
}
ProtectedBinary pbRet = new ProtectedBinary(true, pbTrf32);
MemUtil.ZeroByteArray(pbTrf32);
MemUtil.ZeroByteArray(pbRaw32);
return pbRet;
}
private void ValidateUserKeys()
{
int nAccounts = 0;
foreach(IUserKey uKey in m_vUserKeys)
{
if(uKey is KcpUserAccount)
++nAccounts;
}
if(nAccounts >= 2)
{
Debug.Assert(false);
throw new InvalidOperationException();
}
}
}
public sealed class InvalidCompositeKeyException : Exception
{
public override string Message
{
get
{
return KLRes.InvalidCompositeKey + MessageService.NewParagraph +
KLRes.InvalidCompositeKeyHint;
}
}
/// <summary>
/// Construct a new invalid composite key exception.
/// </summary>
public InvalidCompositeKeyException()
{
}
}
}

View File

@@ -0,0 +1,46 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2019 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
using System;
using ModernKeePassLib.Security;
namespace ModernKeePassLib.Keys
{
/// <summary>
/// Interface to a user key, like a password, key file data, etc.
/// </summary>
public interface IUserKey
{
/// <summary>
/// Get key data. Querying this property is fast (it returns a
/// reference to a cached <c>ProtectedBinary</c> object).
/// If no key data is available, <c>null</c> is returned.
/// </summary>
ProtectedBinary KeyData
{
get;
}
// /// <summary>
// /// Clear the key and securely erase all security-critical information.
// /// </summary>
// void Clear();
}
}

View File

@@ -0,0 +1,68 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2019 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using ModernKeePassLib.Cryptography;
using ModernKeePassLib.Security;
namespace ModernKeePassLib.Keys
{
public sealed class KcpCustomKey : IUserKey
{
private readonly string m_strName;
private ProtectedBinary m_pbKey;
/// <summary>
/// Name of the provider that generated the custom key.
/// </summary>
public string Name
{
get { return m_strName; }
}
public ProtectedBinary KeyData
{
get { return m_pbKey; }
}
public KcpCustomKey(string strName, byte[] pbKeyData, bool bPerformHash)
{
Debug.Assert(strName != null); if(strName == null) throw new ArgumentNullException("strName");
Debug.Assert(pbKeyData != null); if(pbKeyData == null) throw new ArgumentNullException("pbKeyData");
m_strName = strName;
if(bPerformHash)
{
byte[] pbRaw = CryptoUtil.HashSha256(pbKeyData);
m_pbKey = new ProtectedBinary(true, pbRaw);
}
else m_pbKey = new ProtectedBinary(true, pbKeyData);
}
// public void Clear()
// {
// m_pbKey = null;
// }
}
}

View File

@@ -0,0 +1,322 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2019 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
using System;
using System.Diagnostics;
using System.IO;
using System.Security;
using System.Text;
using System.Xml;
#if ModernKeePassLib
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using Windows.Security.Cryptography;
using Windows.Security.Cryptography.Core;
using Windows.Storage;
#else
using System.Security.Cryptography;
#endif
using ModernKeePassLib.Cryptography;
using ModernKeePassLib.Resources;
using ModernKeePassLib.Security;
using ModernKeePassLib.Serialization;
using ModernKeePassLib.Utility;
namespace ModernKeePassLib.Keys
{
/// <summary>
/// Key files as provided by the user.
/// </summary>
public sealed class KcpKeyFile : IUserKey
{
private string m_strPath;
private ProtectedBinary m_pbKeyData;
/// <summary>
/// Path to the key file.
/// </summary>
public string Path
{
get { return m_strPath; }
}
/// <summary>
/// Get key data. Querying this property is fast (it returns a
/// reference to a cached <c>ProtectedBinary</c> object).
/// If no key data is available, <c>null</c> is returned.
/// </summary>
public ProtectedBinary KeyData
{
get { return m_pbKeyData; }
}
#if ModernKeePassLib
public KcpKeyFile(StorageFile strKeyFile)
{
Construct(IOConnectionInfo.FromFile(strKeyFile), false);
}
#else
public KcpKeyFile(string strKeyFile)
{
Construct(IOConnectionInfo.FromPath(strKeyFile), false);
}
public KcpKeyFile(string strKeyFile, bool bThrowIfDbFile)
{
Construct(IOConnectionInfo.FromPath(strKeyFile), bThrowIfDbFile);
}
#endif
public KcpKeyFile(IOConnectionInfo iocKeyFile)
{
Construct(iocKeyFile, false);
}
public KcpKeyFile(IOConnectionInfo iocKeyFile, bool bThrowIfDbFile)
{
Construct(iocKeyFile, bThrowIfDbFile);
}
private void Construct(IOConnectionInfo iocFile, bool bThrowIfDbFile)
{
byte[] pbFileData = IOConnection.ReadFile(iocFile);
if(pbFileData == null) throw new FileNotFoundException();
if(bThrowIfDbFile && (pbFileData.Length >= 8))
{
uint uSig1 = MemUtil.BytesToUInt32(MemUtil.Mid(pbFileData, 0, 4));
uint uSig2 = MemUtil.BytesToUInt32(MemUtil.Mid(pbFileData, 4, 4));
if(((uSig1 == KdbxFile.FileSignature1) &&
(uSig2 == KdbxFile.FileSignature2)) ||
((uSig1 == KdbxFile.FileSignaturePreRelease1) &&
(uSig2 == KdbxFile.FileSignaturePreRelease2)) ||
((uSig1 == KdbxFile.FileSignatureOld1) &&
(uSig2 == KdbxFile.FileSignatureOld2)))
#if KeePassLibSD
throw new Exception(KLRes.KeyFileDbSel);
#else
throw new InvalidDataException(KLRes.KeyFileDbSel);
#endif
}
byte[] pbKey = LoadXmlKeyFile(pbFileData);
if(pbKey == null) pbKey = LoadKeyFile(pbFileData);
if(pbKey == null) throw new InvalidOperationException();
m_strPath = iocFile.Path;
m_pbKeyData = new ProtectedBinary(true, pbKey);
MemUtil.ZeroByteArray(pbKey);
}
// public void Clear()
// {
// m_strPath = string.Empty;
// m_pbKeyData = null;
// }
private static byte[] LoadKeyFile(byte[] pbFileData)
{
if(pbFileData == null) { Debug.Assert(false); return null; }
int iLength = pbFileData.Length;
byte[] pbKey = null;
if(iLength == 32) pbKey = LoadBinaryKey32(pbFileData);
else if(iLength == 64) pbKey = LoadHexKey32(pbFileData);
if(pbKey == null)
pbKey = CryptoUtil.HashSha256(pbFileData);
return pbKey;
}
private static byte[] LoadBinaryKey32(byte[] pbFileData)
{
if(pbFileData == null) { Debug.Assert(false); return null; }
if(pbFileData.Length != 32) { Debug.Assert(false); return null; }
return pbFileData;
}
private static byte[] LoadHexKey32(byte[] pbFileData)
{
if(pbFileData == null) { Debug.Assert(false); return null; }
if(pbFileData.Length != 64) { Debug.Assert(false); return null; }
try
{
if(!StrUtil.IsHexString(pbFileData, true)) return null;
string strHex = StrUtil.Utf8.GetString(pbFileData, 0, pbFileData.Length);
byte[] pbKey = MemUtil.HexStringToByteArray(strHex);
if((pbKey == null) || (pbKey.Length != 32))
{
Debug.Assert(false);
return null;
}
return pbKey;
}
catch(Exception) { Debug.Assert(false); }
return null;
}
/// <summary>
/// Create a new, random key-file.
/// </summary>
/// <param name="strFilePath">Path where the key-file should be saved to.
/// If the file exists already, it will be overwritten.</param>
/// <param name="pbAdditionalEntropy">Additional entropy used to generate
/// the random key. May be <c>null</c> (in this case only the KeePass-internal
/// random number generator is used).</param>
/// <returns>Returns a <c>FileSaveResult</c> error code.</returns>
#if ModernKeePassLib
public static void Create(StorageFile strFilePath, byte[] pbAdditionalEntropy)
#else
public static void Create(string strFilePath, byte[] pbAdditionalEntropy)
#endif
{
byte[] pbKey32 = CryptoRandom.Instance.GetRandomBytes(32);
if(pbKey32 == null) throw new SecurityException();
byte[] pbFinalKey32;
if((pbAdditionalEntropy == null) || (pbAdditionalEntropy.Length == 0))
pbFinalKey32 = pbKey32;
else
{
using(MemoryStream ms = new MemoryStream())
{
MemUtil.Write(ms, pbAdditionalEntropy);
MemUtil.Write(ms, pbKey32);
pbFinalKey32 = CryptoUtil.HashSha256(ms.ToArray());
}
}
CreateXmlKeyFile(strFilePath, pbFinalKey32);
}
// ================================================================
// XML Key Files
// ================================================================
// Sample XML file:
// <?xml version="1.0" encoding="utf-8"?>
// <KeyFile>
// <Meta>
// <Version>1.00</Version>
// </Meta>
// <Key>
// <Data>ySFoKuCcJblw8ie6RkMBdVCnAf4EedSch7ItujK6bmI=</Data>
// </Key>
// </KeyFile>
private const string RootElementName = "KeyFile";
private const string MetaElementName = "Meta";
private const string VersionElementName = "Version";
private const string KeyElementName = "Key";
private const string KeyDataElementName = "Data";
private static byte[] LoadXmlKeyFile(byte[] pbFileData)
{
if(pbFileData == null) { Debug.Assert(false); return null; }
MemoryStream ms = new MemoryStream(pbFileData, false);
byte[] pbKeyData = null;
try
{
XmlDocument doc = XmlUtilEx.CreateXmlDocument();
doc.Load(ms);
XmlElement el = doc.DocumentElement;
if((el == null) || !el.Name.Equals(RootElementName)) return null;
if(el.ChildNodes.Count < 2) return null;
foreach(XmlNode xmlChild in el.ChildNodes)
{
if(xmlChild.Name.Equals(MetaElementName)) { } // Ignore Meta
else if(xmlChild.Name == KeyElementName)
{
foreach(XmlNode xmlKeyChild in xmlChild.ChildNodes)
{
if(xmlKeyChild.Name == KeyDataElementName)
{
if(pbKeyData == null)
pbKeyData = Convert.FromBase64String(xmlKeyChild.InnerText);
}
}
}
}
}
catch(Exception) { pbKeyData = null; }
finally { ms.Dispose(); }
return pbKeyData;
}
#if ModernKeePassLib
private static void CreateXmlKeyFile(StorageFile strFile, byte[] pbKeyData)
#else
private static void CreateXmlKeyFile(string strFile, byte[] pbKeyData)
#endif
{
Debug.Assert(strFile != null);
if(strFile == null) throw new ArgumentNullException("strFile");
Debug.Assert(pbKeyData != null);
if(pbKeyData == null) throw new ArgumentNullException("pbKeyData");
#if ModernKeePassLib
IOConnectionInfo ioc = IOConnectionInfo.FromFile(strFile);
#else
IOConnectionInfo ioc = IOConnectionInfo.FromPath(strFile);
#endif
using(Stream s = IOConnection.OpenWrite(ioc))
{
using(XmlWriter xw = XmlUtilEx.CreateXmlWriter(s))
{
xw.WriteStartDocument();
xw.WriteStartElement(RootElementName); // <KeyFile>
xw.WriteStartElement(MetaElementName); // <Meta>
xw.WriteStartElement(VersionElementName); // <Version>
xw.WriteString("1.00");
xw.WriteEndElement(); // </Version>
xw.WriteEndElement(); // </Meta>
xw.WriteStartElement(KeyElementName); // <Key>
xw.WriteStartElement(KeyDataElementName); // <Data>
xw.WriteString(Convert.ToBase64String(pbKeyData));
xw.WriteEndElement(); // </Data>
xw.WriteEndElement(); // </Key>
xw.WriteEndElement(); // </KeyFile>
xw.WriteEndDocument();
}
}
}
}
}

View File

@@ -0,0 +1,111 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2019 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
using System;
using System.Diagnostics;
using System.Text;
using ModernKeePassLib.Security;
using ModernKeePassLib.Utility;
using ModernKeePassLib.Cryptography;
namespace ModernKeePassLib.Keys
{
/// <summary>
/// Master password/passphrase as provided by the user.
/// </summary>
public sealed class KcpPassword : IUserKey
{
private ProtectedString m_psPassword = null; // Optional
private ProtectedBinary m_pbKeyData;
/// <summary>
/// Get the password as protected string. This is <c>null</c>
/// unless remembering the password has been turned on.
/// </summary>
public ProtectedString Password
{
get { return m_psPassword; }
}
/// <summary>
/// Get key data. Querying this property is fast (it returns a
/// reference to a cached <c>ProtectedBinary</c> object).
/// If no key data is available, <c>null</c> is returned.
/// </summary>
public ProtectedBinary KeyData
{
get { return m_pbKeyData; }
}
public KcpPassword(byte[] pbPasswordUtf8)
{
SetKey(pbPasswordUtf8, true);
}
public KcpPassword(byte[] pbPasswordUtf8, bool bRememberPassword)
{
SetKey(pbPasswordUtf8, bRememberPassword);
}
public KcpPassword(string strPassword)
{
byte[] pb = StrUtil.Utf8.GetBytes(strPassword);
try { SetKey(pb, true); }
finally { MemUtil.ZeroByteArray(pb); }
}
private void SetKey(byte[] pbPasswordUtf8, bool bRememberPassword)
{
Debug.Assert(pbPasswordUtf8 != null);
if(pbPasswordUtf8 == null) throw new ArgumentNullException("pbPasswordUtf8");
#if (DEBUG && !KeePassLibSD)
Debug.Assert(ValidatePassword(pbPasswordUtf8));
#endif
byte[] pbRaw = CryptoUtil.HashSha256(pbPasswordUtf8);
try { m_pbKeyData = new ProtectedBinary(true, pbRaw); }
finally { MemUtil.ZeroByteArray(pbRaw); }
if(bRememberPassword)
m_psPassword = new ProtectedString(true, pbPasswordUtf8);
}
// public void Clear()
// {
// m_psPassword = null;
// m_pbKeyData = null;
// }
#if (DEBUG && !KeePassLibSD)
private static bool ValidatePassword(byte[] pb)
{
try
{
string str = StrUtil.Utf8.GetString(pb);
return str.IsNormalized(NormalizationForm.FormC);
}
catch(Exception) { Debug.Assert(false); }
return false;
}
#endif
}
}

View File

@@ -0,0 +1,167 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2019 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
using System;
using System.Diagnostics;
using System.IO;
using System.Security;
#if ModernKeePassLib
using Windows.Storage;
using System.Security.Cryptography;
#endif
using ModernKeePassLib.Cryptography;
using ModernKeePassLib.Native;
using ModernKeePassLib.Resources;
using ModernKeePassLib.Security;
using ModernKeePassLib.Utility;
namespace ModernKeePassLib.Keys
{
/// <summary>
/// A user key depending on the currently logged on Windows user account.
/// </summary>
public sealed class KcpUserAccount : IUserKey
{
private ProtectedBinary m_pbKeyData = null;
// Constant initialization vector (unique for KeePass)
private static readonly byte[] m_pbEntropy = new byte[] {
0xDE, 0x13, 0x5B, 0x5F, 0x18, 0xA3, 0x46, 0x70,
0xB2, 0x57, 0x24, 0x29, 0x69, 0x88, 0x98, 0xE6
};
private const string UserKeyFileName = "ProtectedUserKey.bin";
/// <summary>
/// Get key data. Querying this property is fast (it returns a
/// reference to a cached <c>ProtectedBinary</c> object).
/// If no key data is available, <c>null</c> is returned.
/// </summary>
public ProtectedBinary KeyData
{
get { return m_pbKeyData; }
}
/// <summary>
/// Construct a user account key.
/// </summary>
public KcpUserAccount()
{
if(!CryptoUtil.IsProtectedDataSupported)
throw new PlatformNotSupportedException(); // Windows 98/ME
byte[] pbKey = LoadUserKey(false);
if(pbKey == null) pbKey = CreateUserKey();
if(pbKey == null) // Should never happen
{
Debug.Assert(false);
throw new SecurityException(KLRes.UserAccountKeyError);
}
m_pbKeyData = new ProtectedBinary(true, pbKey);
MemUtil.ZeroByteArray(pbKey);
}
// public void Clear()
// {
// m_pbKeyData = null;
// }
private static string GetUserKeyFilePath(bool bCreate)
{
#if ModernKeePassLib
string strUserDir = Windows.Storage.ApplicationData.Current.RoamingFolder.Path;
#else
string strUserDir = Environment.GetFolderPath(
Environment.SpecialFolder.ApplicationData);
#endif
strUserDir = UrlUtil.EnsureTerminatingSeparator(strUserDir, false);
strUserDir += PwDefs.ShortProductName;
#if !ModernKeePassLib
if(bCreate && !Directory.Exists(strUserDir))
Directory.CreateDirectory(strUserDir);
#endif
strUserDir = UrlUtil.EnsureTerminatingSeparator(strUserDir, false);
return (strUserDir + UserKeyFileName);
}
private static byte[] LoadUserKey(bool bThrow)
{
byte[] pbKey = null;
#if !KeePassLibSD
try
{
string strFilePath = GetUserKeyFilePath(false);
#if ModernKeePassLib
var fileStream = StorageFile.GetFileFromPathAsync(strFilePath).GetAwaiter().GetResult().OpenStreamForReadAsync().GetAwaiter().GetResult();
var pbProtectedKey = new byte[(int)fileStream.Length];
fileStream.Read(pbProtectedKey, 0, (int)fileStream.Length);
fileStream.Dispose();
#else
byte[] pbProtectedKey = File.ReadAllBytes(strFilePath);
#endif
pbKey = CryptoUtil.UnprotectData(pbProtectedKey, m_pbEntropy,
DataProtectionScope.CurrentUser);
}
catch(Exception)
{
if(bThrow) throw;
pbKey = null;
}
#endif
return pbKey;
}
private static byte[] CreateUserKey()
{
#if KeePassLibSD
return null;
#else
string strFilePath = GetUserKeyFilePath(true);
byte[] pbRandomKey = CryptoRandom.Instance.GetRandomBytes(64);
byte[] pbProtectedKey = CryptoUtil.ProtectData(pbRandomKey,
m_pbEntropy, DataProtectionScope.CurrentUser);
#if ModernKeePassLib
var fileStream = StorageFile.GetFileFromPathAsync(strFilePath).GetAwaiter().GetResult().OpenStreamForWriteAsync().GetAwaiter().GetResult();
fileStream.Write(pbProtectedKey, 0, (int)fileStream.Length);
fileStream.Dispose();
#else
File.WriteAllBytes(strFilePath, pbProtectedKey);
#endif
byte[] pbKey = LoadUserKey(true);
Debug.Assert(MemUtil.ArraysEqual(pbKey, pbRandomKey));
MemUtil.ZeroByteArray(pbRandomKey);
return pbKey;
#endif
}
}
}

View File

@@ -0,0 +1,152 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2019 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
using System;
using System.Collections.Generic;
using System.Text;
using ModernKeePassLib.Serialization;
namespace ModernKeePassLib.Keys
{
public sealed class KeyProviderQueryContext
{
private IOConnectionInfo m_ioInfo;
public IOConnectionInfo DatabaseIOInfo
{
get { return m_ioInfo; }
}
public string DatabasePath
{
get { return m_ioInfo.Path; }
}
private bool m_bCreatingNewKey;
public bool CreatingNewKey
{
get { return m_bCreatingNewKey; }
}
private bool m_bSecDesktop;
public bool IsOnSecureDesktop
{
get { return m_bSecDesktop; }
}
public KeyProviderQueryContext(IOConnectionInfo ioInfo, bool bCreatingNewKey,
bool bOnSecDesktop)
{
if(ioInfo == null) throw new ArgumentNullException("ioInfo");
m_ioInfo = ioInfo.CloneDeep();
m_bCreatingNewKey = bCreatingNewKey;
m_bSecDesktop = bOnSecDesktop;
}
}
public abstract class KeyProvider
{
/// <summary>
/// Name of your key provider (should be unique).
/// </summary>
public abstract string Name
{
get;
}
/// <summary>
/// Property indicating whether the provider is exclusive.
/// If the provider is exclusive, KeePass doesn't allow other
/// key sources (master password, Windows user account, ...)
/// to be combined with the provider.
/// Key providers typically should return <c>false</c>
/// (to allow non-exclusive use), i.e. don't override this
/// property.
/// </summary>
public virtual bool Exclusive
{
get { return false; }
}
/// <summary>
/// Property that specifies whether the returned key data
/// gets hashed by KeePass first or is written directly to
/// the user key data stream.
/// Standard key provider plugins should return <c>false</c>
/// (i.e. don't overwrite this property). Returning <c>true</c>
/// may cause severe security problems and is highly
/// discouraged.
/// </summary>
public virtual bool DirectKey
{
get { return false; }
}
// public virtual PwIcon ImageIndex
// {
// get { return PwIcon.UserKey; }
// }
/// <summary>
/// This property specifies whether the <c>GetKey</c> method might
/// show a form or dialog. If there is any chance that the method shows
/// one, this property must return <c>true</c>. Only if it's guaranteed
/// that the <c>GetKey</c> method doesn't show any form or dialog, this
/// property should return <c>false</c>.
/// </summary>
public virtual bool GetKeyMightShowGui
{
get { return true; }
}
/// <summary>
/// This property specifies whether the key provider is compatible
/// with the secure desktop mode. This almost never is the case,
/// so you usually won't override this property.
/// </summary>
public virtual bool SecureDesktopCompatible
{
get { return false; }
}
public abstract byte[] GetKey(KeyProviderQueryContext ctx);
}
#if DEBUG
public sealed class SampleKeyProvider : KeyProvider
{
public override string Name
{
get { return "Built-In Sample Key Provider"; }
}
// Do not just copy this to your own key provider class! See above.
public override bool GetKeyMightShowGui
{
get { return false; }
}
public override byte[] GetKey(KeyProviderQueryContext ctx)
{
return new byte[]{ 2, 3, 5, 7, 11, 13 };
}
}
#endif
}

View File

@@ -0,0 +1,105 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2019 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
namespace ModernKeePassLib.Keys
{
public sealed class KeyProviderPool : IEnumerable<KeyProvider>
{
private List<KeyProvider> m_vProviders = new List<KeyProvider>();
public int Count
{
get { return m_vProviders.Count; }
}
IEnumerator IEnumerable.GetEnumerator()
{
return m_vProviders.GetEnumerator();
}
public IEnumerator<KeyProvider> GetEnumerator()
{
return m_vProviders.GetEnumerator();
}
public void Add(KeyProvider prov)
{
Debug.Assert(prov != null); if(prov == null) throw new ArgumentNullException("prov");
m_vProviders.Add(prov);
}
public bool Remove(KeyProvider prov)
{
Debug.Assert(prov != null); if(prov == null) throw new ArgumentNullException("prov");
return m_vProviders.Remove(prov);
}
public KeyProvider Get(string strProviderName)
{
if(strProviderName == null) throw new ArgumentNullException("strProviderName");
foreach(KeyProvider prov in m_vProviders)
{
if(prov.Name == strProviderName) return prov;
}
return null;
}
public bool IsKeyProvider(string strName)
{
Debug.Assert(strName != null); if(strName == null) throw new ArgumentNullException("strName");
foreach(KeyProvider prov in m_vProviders)
{
if(prov.Name == strName) return true;
}
return false;
}
internal byte[] GetKey(string strProviderName, KeyProviderQueryContext ctx,
out bool bPerformHash)
{
Debug.Assert(strProviderName != null); if(strProviderName == null) throw new ArgumentNullException("strProviderName");
bPerformHash = true;
foreach(KeyProvider prov in m_vProviders)
{
if(prov.Name == strProviderName)
{
bPerformHash = !prov.DirectKey;
return prov.GetKey(ctx);
}
}
Debug.Assert(false);
return null;
}
}
}

View File

@@ -0,0 +1,51 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2019 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
using System;
using System.Collections.Generic;
using System.Text;
namespace ModernKeePassLib.Keys
{
public enum KeyValidationType
{
MasterPassword = 0
}
public abstract class KeyValidator
{
/// <summary>
/// Name of your key validator (should be unique).
/// </summary>
public abstract string Name
{
get;
}
/// <summary>
/// Validate a key.
/// </summary>
/// <param name="strKey">Key to validate.</param>
/// <param name="t">Type of the validation to perform.</param>
/// <returns>Returns <c>null</c>, if the validation is successful.
/// If there's a problem with the key, the returned string describes
/// the problem.</returns>
public abstract string Validate(string strKey, KeyValidationType t);
}
}

View File

@@ -0,0 +1,86 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2019 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using ModernKeePassLib.Utility;
namespace ModernKeePassLib.Keys
{
public sealed class KeyValidatorPool : IEnumerable<KeyValidator>
{
private List<KeyValidator> m_vValidators = new List<KeyValidator>();
public int Count
{
get { return m_vValidators.Count; }
}
IEnumerator IEnumerable.GetEnumerator()
{
return m_vValidators.GetEnumerator();
}
public IEnumerator<KeyValidator> GetEnumerator()
{
return m_vValidators.GetEnumerator();
}
public void Add(KeyValidator v)
{
Debug.Assert(v != null); if(v == null) throw new ArgumentNullException("v");
m_vValidators.Add(v);
}
public bool Remove(KeyValidator v)
{
Debug.Assert(v != null); if(v == null) throw new ArgumentNullException("v");
return m_vValidators.Remove(v);
}
public string Validate(string strKey, KeyValidationType t)
{
Debug.Assert(strKey != null); if(strKey == null) throw new ArgumentNullException("strKey");
foreach(KeyValidator v in m_vValidators)
{
string strResult = v.Validate(strKey, t);
if(strResult != null) return strResult;
}
return null;
}
public string Validate(byte[] pbKeyUtf8, KeyValidationType t)
{
Debug.Assert(pbKeyUtf8 != null); if(pbKeyUtf8 == null) throw new ArgumentNullException("pbKeyUtf8");
if(m_vValidators.Count == 0) return null;
string strKey = StrUtil.Utf8.GetString(pbKeyUtf8, 0, pbKeyUtf8.Length);
return Validate(strKey, t);
}
}
}

View File

@@ -0,0 +1,33 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2019 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
using System;
namespace ModernKeePassLib.Keys
{
[Flags]
public enum UserKeyType
{
None = 0,
Other = 1,
Password = 2,
KeyFile = 4,
UserAccount = 8
}
}

Binary file not shown.

View File

@@ -0,0 +1,74 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<Version>2.41.0</Version>
<Authors>Geoffroy Bonneville</Authors>
<PackageLicenseUrl>https://www.gnu.org/licenses/gpl-3.0.en.html</PackageLicenseUrl>
<PackageProjectUrl>https://github.com/wismna/ModernKeePass</PackageProjectUrl>
<Description>Portable KeePass Password Management Library that targets .Net Standard and WinRT. Allows reading, editing and writing to KeePass 2.x databases.</Description>
<Company>wismna</Company>
<Product>ModernKeePassLib</Product>
<PackageReleaseNotes>Updated to version 2.41</PackageReleaseNotes>
<PackageTags>KeePass KeePassLib Portable PCL NetStandard ModernKeePass</PackageTags>
<Copyright>Copyright © 2019 Geoffroy Bonneville</Copyright>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DefineConstants>TRACE;ModernKeePassLib</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<DefineConstants>TRACE;ModernKeePassLib</DefineConstants>
</PropertyGroup>
<ItemGroup>
<Compile Remove="Cryptography\Hash\HashAlgorithm.cs" />
<Compile Remove="Cryptography\Hash\HMAC.cs" />
<Compile Remove="Cryptography\Hash\HMACSHA1.cs" />
<Compile Remove="Cryptography\Hash\HMACSHA256.cs" />
<Compile Remove="Cryptography\Hash\SHA256Managed.cs" />
<Compile Remove="Cryptography\Hash\SHA512Managed.cs" />
<Compile Remove="Native\ClipboardU.cs" />
<Compile Remove="Native\NativeLib.cs" />
<Compile Remove="Native\NativeMethods.cs" />
<Compile Remove="Native\NativeMethods.Unix.cs" />
<Compile Remove="Translation\KPControlCustomization.cs" />
<Compile Remove="Translation\KPFormCustomization.cs" />
<Compile Remove="Translation\KPStringTable.cs" />
<Compile Remove="Translation\KPStringTableItem.cs" />
<Compile Remove="Translation\KPTranslation.cs" />
<Compile Remove="Translation\KPTranslationProperties.cs" />
<Compile Remove="Utility\GfxUtil.cs" />
<Compile Remove="Utility\MonoWorkarounds.cs" />
</ItemGroup>
<ItemGroup>
<None Include="Native\ClipboardU.cs" />
<None Include="Native\NativeLib.cs" />
<None Include="Native\NativeMethods.cs" />
<None Include="Native\NativeMethods.Unix.cs" />
<None Include="Translation\KPControlCustomization.cs" />
<None Include="Translation\KPFormCustomization.cs" />
<None Include="Translation\KPStringTable.cs" />
<None Include="Translation\KPStringTableItem.cs" />
<None Include="Translation\KPTranslation.cs" />
<None Include="Translation\KPTranslationProperties.cs" />
<None Include="Utility\GfxUtil.cs" />
<None Include="Utility\MonoWorkarounds.cs" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="System.Runtime.WindowsRuntime" Version="4.3.0" />
<PackageReference Include="System.Security.Cryptography.ProtectedData" Version="4.5.0" />
</ItemGroup>
<ItemGroup>
<Reference Include="Windows">
<HintPath>..\..\..\..\..\..\Program Files (x86)\Windows Kits\10\UnionMetadata\10.0.17134.0\Windows.winmd</HintPath>
<IsWinMDFile>true</IsWinMDFile>
</Reference>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,190 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2019 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Text;
namespace ModernKeePassLib.Native
{
internal static class ClipboardU
{
private const string XSel = "xsel";
private const string XSelV = "--version";
private const string XSelR = "--output --clipboard";
private const string XSelC = "--clear --clipboard";
private const string XSelW = "--input --clipboard";
private const string XSelND = " --nodetach";
private const AppRunFlags XSelWF = AppRunFlags.WaitForExit;
private static bool? g_obXSel = null;
public static string GetText()
{
// System.Windows.Forms.Clipboard doesn't work properly,
// see Mono workaround 1530
// string str = GtkGetText();
// if(str != null) return str;
return XSelGetText();
}
public static bool SetText(string strText, bool bMayBlock)
{
string str = (strText ?? string.Empty);
// System.Windows.Forms.Clipboard doesn't work properly,
// see Mono workaround 1530
// if(GtkSetText(str)) return true;
return XSelSetText(str, bMayBlock);
}
// =============================================================
// LibGTK
// Even though GTK+ 3 appears to be loaded already, performing a
// P/Invoke of LibGTK's gtk_init_check function terminates the
// process (!) with the following error message:
// "Gtk-ERROR **: GTK+ 2.x symbols detected. Using GTK+ 2.x and
// GTK+ 3 in the same process is not supported".
/* private static bool GtkInit()
{
try
{
// GTK requires GLib;
// the following throws if and only if GLib is unavailable
NativeMethods.g_free(IntPtr.Zero);
if(NativeMethods.gtk_init_check(IntPtr.Zero, IntPtr.Zero) !=
NativeMethods.G_FALSE)
return true;
Debug.Assert(false);
}
catch(Exception) { Debug.Assert(false); }
return false;
}
private static string GtkGetText()
{
IntPtr lpText = IntPtr.Zero;
try
{
if(GtkInit())
{
IntPtr h = NativeMethods.gtk_clipboard_get(
NativeMethods.GDK_SELECTION_CLIPBOARD);
if(h != IntPtr.Zero)
{
lpText = NativeMethods.gtk_clipboard_wait_for_text(h);
if(lpText != IntPtr.Zero)
return NativeMethods.Utf8ZToString(lpText);
}
}
}
catch(Exception) { Debug.Assert(false); }
finally
{
try { NativeMethods.g_free(lpText); }
catch(Exception) { Debug.Assert(false); }
}
return null;
}
private static bool GtkSetText(string str)
{
IntPtr lpText = IntPtr.Zero;
try
{
if(GtkInit())
{
lpText = NativeMethods.Utf8ZFromString(str ?? string.Empty);
if(lpText == IntPtr.Zero) { Debug.Assert(false); return false; }
bool b = false;
for(int i = 0; i < 2; ++i)
{
IntPtr h = NativeMethods.gtk_clipboard_get((i == 0) ?
NativeMethods.GDK_SELECTION_PRIMARY :
NativeMethods.GDK_SELECTION_CLIPBOARD);
if(h != IntPtr.Zero)
{
NativeMethods.gtk_clipboard_clear(h);
NativeMethods.gtk_clipboard_set_text(h, lpText, -1);
NativeMethods.gtk_clipboard_store(h);
b = true;
}
}
return b;
}
}
catch(Exception) { Debug.Assert(false); }
finally { NativeMethods.Utf8ZFree(lpText); }
return false;
} */
// =============================================================
// XSel
private static bool XSelInit()
{
if(g_obXSel.HasValue) return g_obXSel.Value;
string strTest = NativeLib.RunConsoleApp(XSel, XSelV);
bool b = (strTest != null);
g_obXSel = b;
return b;
}
private static string XSelGetText()
{
if(!XSelInit()) return null;
return NativeLib.RunConsoleApp(XSel, XSelR);
}
private static bool XSelSetText(string str, bool bMayBlock)
{
if(!XSelInit()) return false;
string strOpt = (bMayBlock ? XSelND : string.Empty);
// xsel with an empty input can hang, thus use --clear
if(str.Length == 0)
return (NativeLib.RunConsoleApp(XSel, XSelC + strOpt,
null, XSelWF) != null);
// Use --nodetach to prevent clipboard corruption;
// https://sourceforge.net/p/keepass/bugs/1603/
return (NativeLib.RunConsoleApp(XSel, XSelW + strOpt,
str, XSelWF) != null);
}
}
}

View File

@@ -0,0 +1,90 @@
using System;
namespace ModernKeePassLib.Native
{
internal static class NativeLib
{
public static ulong MonoVersion {
get { throw new NotImplementedException(); }
}
public static bool IsUnix()
{
return true;
}
public static bool TransformKey256(byte[] pbNative, byte[] pbSeed, ulong uRounds)
{
return false;
}
public static System.PlatformID GetPlatformID()
{
return Environment.OSVersion.Platform;
}
}
internal static class NativeMethods
{
public static bool SupportsStrCmpNaturally => false;
internal const int GCRY_CIPHER_AES256 = 9;
internal const int GCRY_CIPHER_MODE_ECB = 1;
public static int StrCmpNaturally (string s1, string s2)
{
throw new NotImplementedException();
}
internal static void gcry_check_version(IntPtr zero)
{
throw new NotImplementedException();
}
public static void gcry_cipher_open(ref IntPtr intPtr, object gcryCipherAes256, object gcryCipherModeEcb, int i)
{
throw new NotImplementedException();
}
internal static int gcry_cipher_setkey(IntPtr h, IntPtr pSeed32, IntPtr n32)
{
throw new NotImplementedException();
}
internal static void gcry_cipher_close(IntPtr h)
{
throw new NotImplementedException();
}
internal static int gcry_cipher_encrypt(IntPtr h, IntPtr pData32, IntPtr n32, IntPtr zero1, IntPtr zero2)
{
throw new NotImplementedException();
}
public static string GetUserRuntimeDir()
{
throw new NotImplementedException();
}
}
internal enum MemoryProtectionScope
{
CrossProcess,
SameLogon,
SameProcess
}
internal static class ProtectedMemory
{
public static byte[] Protect(byte[] userData, MemoryProtectionScope scope)
{
throw new NotImplementedException();
}
public static byte[] Unprotect(byte[] userData, MemoryProtectionScope scope)
{
throw new NotImplementedException();
}
}
}

View File

@@ -0,0 +1,460 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2019 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.RegularExpressions;
#if !KeePassUAP
using System.IO;
using System.Threading;
#if !ModernKeePassLib
using System.Windows.Forms;
#endif
#endif
using ModernKeePassLib.Utility;
namespace ModernKeePassLib.Native
{
/// <summary>
/// Interface to native library (library containing fast versions of
/// several cryptographic functions).
/// </summary>
public static class NativeLib
{
private static bool m_bAllowNative = true;
/// <summary>
/// If this property is set to <c>true</c>, the native library is used.
/// If it is <c>false</c>, all calls to functions in this class will fail.
/// </summary>
public static bool AllowNative
{
get { return m_bAllowNative; }
set { m_bAllowNative = value; }
}
private static ulong? m_ouMonoVersion = null;
public static ulong MonoVersion
{
get
{
if(m_ouMonoVersion.HasValue) return m_ouMonoVersion.Value;
ulong uVersion = 0;
try
{
Type t = Type.GetType("Mono.Runtime");
if(t != null)
{
MethodInfo mi = t.GetMethod("GetDisplayName",
BindingFlags.NonPublic | BindingFlags.Static);
if(mi != null)
{
string strName = (mi.Invoke(null, null) as string);
if(!string.IsNullOrEmpty(strName))
{
Match m = Regex.Match(strName, "\\d+(\\.\\d+)+");
if(m.Success)
uVersion = StrUtil.ParseVersion(m.Value);
else { Debug.Assert(false); }
}
else { Debug.Assert(false); }
}
else { Debug.Assert(false); }
}
}
catch(Exception) { Debug.Assert(false); }
m_ouMonoVersion = uVersion;
return uVersion;
}
}
/// <summary>
/// Determine if the native library is installed.
/// </summary>
/// <returns>Returns <c>true</c>, if the native library is installed.</returns>
public static bool IsLibraryInstalled()
{
byte[] pDummy0 = new byte[32];
byte[] pDummy1 = new byte[32];
// Save the native state
bool bCachedNativeState = m_bAllowNative;
// Temporarily allow native functions and try to load the library
m_bAllowNative = true;
bool bResult = TransformKey256(pDummy0, pDummy1, 16);
// Pop native state and return result
m_bAllowNative = bCachedNativeState;
return bResult;
}
private static bool? m_bIsUnix = null;
public static bool IsUnix()
{
if(m_bIsUnix.HasValue) return m_bIsUnix.Value;
PlatformID p = GetPlatformID();
// Mono defines Unix as 128 in early .NET versions
#if !KeePassLibSD
m_bIsUnix = ((p == PlatformID.Unix) || (p == PlatformID.MacOSX) ||
((int)p == 128));
#else
m_bIsUnix = (((int)p == 4) || ((int)p == 6) || ((int)p == 128));
#endif
return m_bIsUnix.Value;
}
private static PlatformID? m_platID = null;
public static PlatformID GetPlatformID()
{
if(m_platID.HasValue) return m_platID.Value;
#if KeePassUAP
m_platID = EnvironmentExt.OSVersion.Platform;
#else
m_platID = Environment.OSVersion.Platform;
#endif
#if (!KeePassLibSD && !KeePassUAP)
// Mono returns PlatformID.Unix on Mac OS X, workaround this
if(m_platID.Value == PlatformID.Unix)
{
if((RunConsoleApp("uname", null) ?? string.Empty).Trim().Equals(
"Darwin", StrUtil.CaseIgnoreCmp))
m_platID = PlatformID.MacOSX;
}
#endif
return m_platID.Value;
}
private static DesktopType? m_tDesktop = null;
public static DesktopType GetDesktopType()
{
if(!m_tDesktop.HasValue)
{
DesktopType t = DesktopType.None;
if(!IsUnix()) t = DesktopType.Windows;
else
{
try
{
string strXdg = (Environment.GetEnvironmentVariable(
"XDG_CURRENT_DESKTOP") ?? string.Empty).Trim();
string strGdm = (Environment.GetEnvironmentVariable(
"GDMSESSION") ?? string.Empty).Trim();
StringComparison sc = StrUtil.CaseIgnoreCmp;
if(strXdg.Equals("Unity", sc))
t = DesktopType.Unity;
else if(strXdg.Equals("LXDE", sc))
t = DesktopType.Lxde;
else if(strXdg.Equals("XFCE", sc))
t = DesktopType.Xfce;
else if(strXdg.Equals("MATE", sc))
t = DesktopType.Mate;
else if(strXdg.Equals("X-Cinnamon", sc)) // Mint 18.3
t = DesktopType.Cinnamon;
else if(strXdg.Equals("Pantheon", sc)) // Elementary OS
t = DesktopType.Pantheon;
else if(strXdg.Equals("KDE", sc) || // Mint 16, Kubuntu 17.10
strGdm.Equals("kde-plasma", sc)) // Ubuntu 12.04
t = DesktopType.Kde;
else if(strXdg.Equals("GNOME", sc))
{
if(strGdm.Equals("cinnamon", sc)) // Mint 13
t = DesktopType.Cinnamon;
else t = DesktopType.Gnome; // Fedora 27
}
else if(strXdg.Equals("ubuntu:GNOME", sc))
t = DesktopType.Gnome;
}
catch(Exception) { Debug.Assert(false); }
}
m_tDesktop = t;
}
return m_tDesktop.Value;
}
#if (!KeePassLibSD && !KeePassUAP)
public static string RunConsoleApp(string strAppPath, string strParams)
{
return RunConsoleApp(strAppPath, strParams, null);
}
public static string RunConsoleApp(string strAppPath, string strParams,
string strStdInput)
{
return RunConsoleApp(strAppPath, strParams, strStdInput,
(AppRunFlags.GetStdOutput | AppRunFlags.WaitForExit));
}
private delegate string RunProcessDelegate();
public static string RunConsoleApp(string strAppPath, string strParams,
string strStdInput, AppRunFlags f)
{
if(strAppPath == null) throw new ArgumentNullException("strAppPath");
if(strAppPath.Length == 0) throw new ArgumentException("strAppPath");
bool bStdOut = ((f & AppRunFlags.GetStdOutput) != AppRunFlags.None);
RunProcessDelegate fnRun = delegate()
{
Process pToDispose = null;
try
{
ProcessStartInfo psi = new ProcessStartInfo();
psi.CreateNoWindow = true;
psi.FileName = strAppPath;
psi.WindowStyle = ProcessWindowStyle.Hidden;
psi.UseShellExecute = false;
psi.RedirectStandardOutput = bStdOut;
if(strStdInput != null) psi.RedirectStandardInput = true;
if(!string.IsNullOrEmpty(strParams)) psi.Arguments = strParams;
Process p = Process.Start(psi);
pToDispose = p;
if(strStdInput != null)
{
EnsureNoBom(p.StandardInput);
p.StandardInput.Write(strStdInput);
p.StandardInput.Close();
}
string strOutput = string.Empty;
if(bStdOut) strOutput = p.StandardOutput.ReadToEnd();
if((f & AppRunFlags.WaitForExit) != AppRunFlags.None)
p.WaitForExit();
else if((f & AppRunFlags.GCKeepAlive) != AppRunFlags.None)
{
pToDispose = null; // Thread disposes it
Thread th = new Thread(delegate()
{
try { p.WaitForExit(); p.Dispose(); }
catch(Exception) { Debug.Assert(false); }
});
th.Start();
}
return strOutput;
}
#if DEBUG
catch(Exception ex) { Debug.Assert(ex is ThreadAbortException); }
#else
catch(Exception) { }
#endif
finally
{
try { if(pToDispose != null) pToDispose.Dispose(); }
catch(Exception) { Debug.Assert(false); }
}
return null;
};
#if !ModernKeePassLib
if((f & AppRunFlags.DoEvents) != AppRunFlags.None)
{
List<Form> lDisabledForms = new List<Form>();
if((f & AppRunFlags.DisableForms) != AppRunFlags.None)
{
foreach(Form form in Application.OpenForms)
{
if(!form.Enabled) continue;
lDisabledForms.Add(form);
form.Enabled = false;
}
}
IAsyncResult ar = fnRun.BeginInvoke(null, null);
while(!ar.AsyncWaitHandle.WaitOne(0))
{
Application.DoEvents();
Thread.Sleep(2);
}
string strRet = fnRun.EndInvoke(ar);
for(int i = lDisabledForms.Count - 1; i >= 0; --i)
lDisabledForms[i].Enabled = true;
return strRet;
}
#endif
return fnRun();
}
private static void EnsureNoBom(StreamWriter sw)
{
if(sw == null) { Debug.Assert(false); return; }
if(!MonoWorkarounds.IsRequired(1219)) return;
try
{
Encoding enc = sw.Encoding;
if(enc == null) { Debug.Assert(false); return; }
byte[] pbBom = enc.GetPreamble();
if((pbBom == null) || (pbBom.Length == 0)) return;
// For Mono >= 4.0 (using Microsoft's reference source)
try
{
FieldInfo fi = typeof(StreamWriter).GetField("haveWrittenPreamble",
BindingFlags.Instance | BindingFlags.NonPublic);
if(fi != null)
{
fi.SetValue(sw, true);
return;
}
}
catch(Exception) { Debug.Assert(false); }
// For Mono < 4.0
FieldInfo fiPD = typeof(StreamWriter).GetField("preamble_done",
BindingFlags.Instance | BindingFlags.NonPublic);
if(fiPD != null) fiPD.SetValue(sw, true);
else { Debug.Assert(false); }
}
catch(Exception) { Debug.Assert(false); }
}
#endif
/// <summary>
/// Transform a key.
/// </summary>
/// <param name="pBuf256">Source and destination buffer.</param>
/// <param name="pKey256">Key to use in the transformation.</param>
/// <param name="uRounds">Number of transformation rounds.</param>
/// <returns>Returns <c>true</c>, if the key was transformed successfully.</returns>
public static bool TransformKey256(byte[] pBuf256, byte[] pKey256,
ulong uRounds)
{
#if KeePassUAP || ModernKeePassLib
return false;
#else
if(!m_bAllowNative) return false;
KeyValuePair<IntPtr, IntPtr> kvp = PrepareArrays256(pBuf256, pKey256);
bool bResult = false;
try
{
bResult = NativeMethods.TransformKey(kvp.Key, kvp.Value, uRounds);
}
catch(Exception) { bResult = false; }
if(bResult) GetBuffers256(kvp, pBuf256, pKey256);
FreeArrays(kvp);
return bResult;
#endif
}
/// <summary>
/// Benchmark key transformation.
/// </summary>
/// <param name="uTimeMs">Number of milliseconds to perform the benchmark.</param>
/// <param name="puRounds">Number of transformations done.</param>
/// <returns>Returns <c>true</c>, if the benchmark was successful.</returns>
public static bool TransformKeyBenchmark256(uint uTimeMs, out ulong puRounds)
{
puRounds = 0;
#if KeePassUAP || ModernKeePassLib
return false;
#else
if(!m_bAllowNative) return false;
try { puRounds = NativeMethods.TransformKeyBenchmark(uTimeMs); }
catch(Exception) { return false; }
return true;
#endif
}
private static KeyValuePair<IntPtr, IntPtr> PrepareArrays256(byte[] pBuf256,
byte[] pKey256)
{
Debug.Assert((pBuf256 != null) && (pBuf256.Length == 32));
if(pBuf256 == null) throw new ArgumentNullException("pBuf256");
if(pBuf256.Length != 32) throw new ArgumentException();
Debug.Assert((pKey256 != null) && (pKey256.Length == 32));
if(pKey256 == null) throw new ArgumentNullException("pKey256");
if(pKey256.Length != 32) throw new ArgumentException();
IntPtr hBuf = Marshal.AllocHGlobal(pBuf256.Length);
Marshal.Copy(pBuf256, 0, hBuf, pBuf256.Length);
IntPtr hKey = Marshal.AllocHGlobal(pKey256.Length);
Marshal.Copy(pKey256, 0, hKey, pKey256.Length);
return new KeyValuePair<IntPtr, IntPtr>(hBuf, hKey);
}
private static void GetBuffers256(KeyValuePair<IntPtr, IntPtr> kvpSource,
byte[] pbDestBuf, byte[] pbDestKey)
{
if(kvpSource.Key != IntPtr.Zero)
Marshal.Copy(kvpSource.Key, pbDestBuf, 0, pbDestBuf.Length);
if(kvpSource.Value != IntPtr.Zero)
Marshal.Copy(kvpSource.Value, pbDestKey, 0, pbDestKey.Length);
}
private static void FreeArrays(KeyValuePair<IntPtr, IntPtr> kvpPointers)
{
if(kvpPointers.Key != IntPtr.Zero)
Marshal.FreeHGlobal(kvpPointers.Key);
if(kvpPointers.Value != IntPtr.Zero)
Marshal.FreeHGlobal(kvpPointers.Value);
}
internal static Type GetUwpType(string strType)
{
if(string.IsNullOrEmpty(strType)) { Debug.Assert(false); return null; }
// https://referencesource.microsoft.com/#mscorlib/system/runtime/interopservices/windowsruntime/winrtclassactivator.cs
return Type.GetType(strType + ", Windows, ContentType=WindowsRuntime", false);
}
}
}

View File

@@ -0,0 +1,216 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2019 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
#if !KeePassUAP
using System.Windows.Forms;
#endif
namespace ModernKeePassLib.Native
{
internal static partial class NativeMethods
{
#if (!KeePassLibSD && !KeePassUAP)
[StructLayout(LayoutKind.Sequential)]
private struct XClassHint
{
public IntPtr res_name;
public IntPtr res_class;
}
[DllImport("libX11")]
private static extern int XSetClassHint(IntPtr display, IntPtr window, IntPtr class_hints);
private static Type m_tXplatUIX11 = null;
private static Type GetXplatUIX11Type(bool bThrowOnError)
{
if(m_tXplatUIX11 == null)
{
// CheckState is in System.Windows.Forms
string strTypeCS = typeof(CheckState).AssemblyQualifiedName;
string strTypeX11 = strTypeCS.Replace("CheckState", "XplatUIX11");
m_tXplatUIX11 = Type.GetType(strTypeX11, bThrowOnError, true);
}
return m_tXplatUIX11;
}
private static Type m_tHwnd = null;
private static Type GetHwndType(bool bThrowOnError)
{
if(m_tHwnd == null)
{
// CheckState is in System.Windows.Forms
string strTypeCS = typeof(CheckState).AssemblyQualifiedName;
string strTypeHwnd = strTypeCS.Replace("CheckState", "Hwnd");
m_tHwnd = Type.GetType(strTypeHwnd, bThrowOnError, true);
}
return m_tHwnd;
}
internal static void SetWmClass(Form f, string strName, string strClass)
{
if(f == null) { Debug.Assert(false); return; }
// The following crashes under Mac OS X (SIGSEGV in native code,
// not just an exception), thus skip it when we're on Mac OS X;
// https://sourceforge.net/projects/keepass/forums/forum/329221/topic/5860588
if(NativeLib.GetPlatformID() == PlatformID.MacOSX) return;
try
{
Type tXplatUIX11 = GetXplatUIX11Type(true);
FieldInfo fiDisplayHandle = tXplatUIX11.GetField("DisplayHandle",
BindingFlags.NonPublic | BindingFlags.Static);
IntPtr hDisplay = (IntPtr)fiDisplayHandle.GetValue(null);
Type tHwnd = GetHwndType(true);
MethodInfo miObjectFromHandle = tHwnd.GetMethod("ObjectFromHandle",
BindingFlags.Public | BindingFlags.Static);
object oHwnd = miObjectFromHandle.Invoke(null, new object[] { f.Handle });
FieldInfo fiWholeWindow = tHwnd.GetField("whole_window",
BindingFlags.NonPublic | BindingFlags.Instance);
IntPtr hWindow = (IntPtr)fiWholeWindow.GetValue(oHwnd);
XClassHint xch = new XClassHint();
xch.res_name = Marshal.StringToCoTaskMemAnsi(strName ?? string.Empty);
xch.res_class = Marshal.StringToCoTaskMemAnsi(strClass ?? string.Empty);
IntPtr pXch = Marshal.AllocCoTaskMem(Marshal.SizeOf(xch));
Marshal.StructureToPtr(xch, pXch, false);
XSetClassHint(hDisplay, hWindow, pXch);
Marshal.FreeCoTaskMem(pXch);
Marshal.FreeCoTaskMem(xch.res_name);
Marshal.FreeCoTaskMem(xch.res_class);
}
catch(Exception) { Debug.Assert(false); }
}
#endif
// =============================================================
// LibGCrypt 1.8.1
private const string LibGCrypt = "libgcrypt.so.20";
internal const int GCRY_CIPHER_AES256 = 9;
internal const int GCRY_CIPHER_MODE_ECB = 1;
[DllImport(LibGCrypt)]
internal static extern IntPtr gcry_check_version(IntPtr lpReqVersion);
[DllImport(LibGCrypt)]
internal static extern uint gcry_cipher_open(ref IntPtr ph, int nAlgo,
int nMode, uint uFlags);
[DllImport(LibGCrypt)]
internal static extern void gcry_cipher_close(IntPtr h);
[DllImport(LibGCrypt)]
internal static extern uint gcry_cipher_setkey(IntPtr h, IntPtr pbKey,
IntPtr cbKey); // cbKey is size_t
[DllImport(LibGCrypt)]
internal static extern uint gcry_cipher_encrypt(IntPtr h, IntPtr pbOut,
IntPtr cbOut, IntPtr pbIn, IntPtr cbIn); // cb* are size_t
/* internal static IntPtr Utf8ZFromString(string str)
{
byte[] pb = StrUtil.Utf8.GetBytes(str ?? string.Empty);
IntPtr p = Marshal.AllocCoTaskMem(pb.Length + 1);
if(p != IntPtr.Zero)
{
Marshal.Copy(pb, 0, p, pb.Length);
Marshal.WriteByte(p, pb.Length, 0);
}
else { Debug.Assert(false); }
return p;
}
internal static string Utf8ZToString(IntPtr p)
{
if(p == IntPtr.Zero) { Debug.Assert(false); return null; }
List<byte> l = new List<byte>();
for(int i = 0; i < int.MaxValue; ++i)
{
byte bt = Marshal.ReadByte(p, i);
if(bt == 0) break;
l.Add(bt);
}
return StrUtil.Utf8.GetString(l.ToArray());
}
internal static void Utf8ZFree(IntPtr p)
{
if(p != IntPtr.Zero) Marshal.FreeCoTaskMem(p);
} */
/* // =============================================================
// LibGLib 2
private const string LibGLib = "libglib-2.0.so.0";
internal const int G_FALSE = 0;
// https://developer.gnome.org/glib/stable/glib-Memory-Allocation.html
[DllImport(LibGLib)]
internal static extern void g_free(IntPtr pMem); // pMem may be null
// =============================================================
// LibGTK 3 (3.22.11 / 3.22.24)
private const string LibGtk = "libgtk-3.so.0";
internal static readonly IntPtr GDK_SELECTION_PRIMARY = new IntPtr(1);
internal static readonly IntPtr GDK_SELECTION_CLIPBOARD = new IntPtr(69);
[DllImport(LibGtk)]
internal static extern int gtk_init_check(IntPtr pArgc, IntPtr pArgv);
[DllImport(LibGtk)]
// The returned handle is owned by GTK and must not be freed
internal static extern IntPtr gtk_clipboard_get(IntPtr pSelection);
[DllImport(LibGtk)]
internal static extern void gtk_clipboard_clear(IntPtr hClipboard);
[DllImport(LibGtk)]
internal static extern IntPtr gtk_clipboard_wait_for_text(IntPtr hClipboard);
[DllImport(LibGtk)]
internal static extern void gtk_clipboard_set_text(IntPtr hClipboard,
IntPtr lpText, int cbLen);
[DllImport(LibGtk)]
internal static extern void gtk_clipboard_store(IntPtr hClipboard); */
}
}

View File

@@ -0,0 +1,260 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2019 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using ModernKeePassLib.Utility;
namespace ModernKeePassLib.Native
{
internal static partial class NativeMethods
{
internal const int MAX_PATH = 260;
internal const long INVALID_HANDLE_VALUE = -1;
internal const uint MOVEFILE_REPLACE_EXISTING = 0x00000001;
internal const uint MOVEFILE_COPY_ALLOWED = 0x00000002;
internal const uint FILE_SUPPORTS_TRANSACTIONS = 0x00200000;
internal const int MAX_TRANSACTION_DESCRIPTION_LENGTH = 64;
// internal const uint TF_SFT_SHOWNORMAL = 0x00000001;
// internal const uint TF_SFT_HIDDEN = 0x00000008;
/* [DllImport("KeePassNtv32.dll", EntryPoint = "TransformKey")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool TransformKey32(IntPtr pBuf256,
IntPtr pKey256, UInt64 uRounds);
[DllImport("KeePassNtv64.dll", EntryPoint = "TransformKey")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool TransformKey64(IntPtr pBuf256,
IntPtr pKey256, UInt64 uRounds);
internal static bool TransformKey(IntPtr pBuf256, IntPtr pKey256,
UInt64 uRounds)
{
if(IntPtr.Size == 4)
return TransformKey32(pBuf256, pKey256, uRounds);
return TransformKey64(pBuf256, pKey256, uRounds);
}
[DllImport("KeePassNtv32.dll", EntryPoint = "TransformKeyTimed")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool TransformKeyTimed32(IntPtr pBuf256,
IntPtr pKey256, ref UInt64 puRounds, UInt32 uSeconds);
[DllImport("KeePassNtv64.dll", EntryPoint = "TransformKeyTimed")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool TransformKeyTimed64(IntPtr pBuf256,
IntPtr pKey256, ref UInt64 puRounds, UInt32 uSeconds);
internal static bool TransformKeyTimed(IntPtr pBuf256, IntPtr pKey256,
ref UInt64 puRounds, UInt32 uSeconds)
{
if(IntPtr.Size == 4)
return TransformKeyTimed32(pBuf256, pKey256, ref puRounds, uSeconds);
return TransformKeyTimed64(pBuf256, pKey256, ref puRounds, uSeconds);
} */
#if !KeePassUAP
[DllImport("KeePassLibC32.dll", EntryPoint = "TransformKey256")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool TransformKey32(IntPtr pBuf256,
IntPtr pKey256, UInt64 uRounds);
[DllImport("KeePassLibC64.dll", EntryPoint = "TransformKey256")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool TransformKey64(IntPtr pBuf256,
IntPtr pKey256, UInt64 uRounds);
internal static bool TransformKey(IntPtr pBuf256, IntPtr pKey256,
UInt64 uRounds)
{
if(IntPtr.Size == 4)
return TransformKey32(pBuf256, pKey256, uRounds);
return TransformKey64(pBuf256, pKey256, uRounds);
}
[DllImport("KeePassLibC32.dll", EntryPoint = "TransformKeyBenchmark256")]
private static extern UInt64 TransformKeyBenchmark32(UInt32 uTimeMs);
[DllImport("KeePassLibC64.dll", EntryPoint = "TransformKeyBenchmark256")]
private static extern UInt64 TransformKeyBenchmark64(UInt32 uTimeMs);
internal static UInt64 TransformKeyBenchmark(UInt32 uTimeMs)
{
if(IntPtr.Size == 4)
return TransformKeyBenchmark32(uTimeMs);
return TransformKeyBenchmark64(uTimeMs);
}
#endif
/* [DllImport("KeePassLibC32.dll", EntryPoint = "TF_ShowLangBar")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool TF_ShowLangBar32(UInt32 dwFlags);
[DllImport("KeePassLibC64.dll", EntryPoint = "TF_ShowLangBar")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool TF_ShowLangBar64(UInt32 dwFlags);
internal static bool TfShowLangBar(uint dwFlags)
{
if(IntPtr.Size == 4) return TF_ShowLangBar32(dwFlags);
return TF_ShowLangBar64(dwFlags);
} */
[DllImport("KeePassLibC32.dll", EntryPoint = "ProtectProcessWithDacl")]
private static extern void ProtectProcessWithDacl32();
[DllImport("KeePassLibC64.dll", EntryPoint = "ProtectProcessWithDacl")]
private static extern void ProtectProcessWithDacl64();
internal static void ProtectProcessWithDacl()
{
try
{
if(NativeLib.IsUnix()) return;
if(IntPtr.Size == 4) ProtectProcessWithDacl32();
else ProtectProcessWithDacl64();
}
catch(Exception) { Debug.Assert(false); }
}
[DllImport("Kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool CloseHandle(IntPtr hObject);
[DllImport("Kernel32.dll", CharSet = CharSet.Auto, ExactSpelling = false,
SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool GetVolumeInformation(string lpRootPathName,
StringBuilder lpVolumeNameBuffer, UInt32 nVolumeNameSize,
ref UInt32 lpVolumeSerialNumber, ref UInt32 lpMaximumComponentLength,
ref UInt32 lpFileSystemFlags, StringBuilder lpFileSystemNameBuffer,
UInt32 nFileSystemNameSize);
[DllImport("Kernel32.dll", CharSet = CharSet.Auto, ExactSpelling = false,
SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool MoveFileEx(string lpExistingFileName,
string lpNewFileName, UInt32 dwFlags);
[DllImport("KtmW32.dll", CharSet = CharSet.Unicode, ExactSpelling = true,
SetLastError = true)]
internal static extern IntPtr CreateTransaction(IntPtr lpTransactionAttributes,
IntPtr lpUOW, UInt32 dwCreateOptions, UInt32 dwIsolationLevel,
UInt32 dwIsolationFlags, UInt32 dwTimeout, string lpDescription);
[DllImport("KtmW32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool CommitTransaction(IntPtr hTransaction);
[DllImport("Kernel32.dll", CharSet = CharSet.Auto, ExactSpelling = false,
SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool MoveFileTransacted(string lpExistingFileName,
string lpNewFileName, IntPtr lpProgressRoutine, IntPtr lpData,
UInt32 dwFlags, IntPtr hTransaction);
#if (!KeePassLibSD && !KeePassUAP)
[DllImport("ShlWApi.dll", CharSet = CharSet.Auto)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool PathRelativePathTo([Out] StringBuilder pszPath,
[In] string pszFrom, uint dwAttrFrom, [In] string pszTo, uint dwAttrTo);
[DllImport("ShlWApi.dll", CharSet = CharSet.Unicode, ExactSpelling = true)]
private static extern int StrCmpLogicalW(string x, string y);
private static bool? m_obSupportsLogicalCmp = null;
private static void TestNaturalComparisonsSupport()
{
try
{
StrCmpLogicalW("0", "0"); // Throws exception if unsupported
m_obSupportsLogicalCmp = true;
}
catch(Exception) { m_obSupportsLogicalCmp = false; }
}
#endif
internal static bool SupportsStrCmpNaturally
{
get
{
#if (!KeePassLibSD && !KeePassUAP)
if(!m_obSupportsLogicalCmp.HasValue)
TestNaturalComparisonsSupport();
return m_obSupportsLogicalCmp.Value;
#else
return false;
#endif
}
}
internal static int StrCmpNaturally(string x, string y)
{
#if (!KeePassLibSD && !KeePassUAP)
if(!NativeMethods.SupportsStrCmpNaturally)
{
Debug.Assert(false);
return string.Compare(x, y, true);
}
return StrCmpLogicalW(x, y);
#else
Debug.Assert(false);
return string.Compare(x, y, true);
#endif
}
internal static string GetUserRuntimeDir()
{
#if KeePassLibSD
return Path.GetTempPath();
#else
#if KeePassUAP
string strRtDir = EnvironmentExt.AppDataLocalFolderPath;
#else
string strRtDir = Environment.GetEnvironmentVariable("XDG_RUNTIME_DIR");
if(string.IsNullOrEmpty(strRtDir))
strRtDir = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
if(string.IsNullOrEmpty(strRtDir))
{
Debug.Assert(false);
return Path.GetTempPath(); // Not UrlUtil (otherwise cyclic)
}
#endif
strRtDir = UrlUtil.EnsureTerminatingSeparator(strRtDir, false);
strRtDir += PwDefs.ShortProductName;
return strRtDir;
#endif
}
}
}

View File

@@ -0,0 +1,127 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2019 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
using System;
using System.Collections.Generic;
using System.Diagnostics;
#if !ModernKeePassLib
using System.Drawing;
#else
using Windows.UI.Xaml.Controls;
#endif
using ModernKeePassLib.Utility;
namespace ModernKeePassLib
{
/// <summary>
/// Custom icon. <c>PwCustomIcon</c> objects are immutable.
/// </summary>
public sealed class PwCustomIcon
{
private readonly PwUuid m_pwUuid;
private readonly byte[] m_pbImageDataPng;
private readonly Image m_imgOrg;
private Dictionary<long, Image> m_dImageCache = new Dictionary<long, Image>();
// Recommended maximum sizes, not obligatory
internal const int MaxWidth = 128;
internal const int MaxHeight = 128;
public PwUuid Uuid
{
get { return m_pwUuid; }
}
public byte[] ImageDataPng
{
get { return m_pbImageDataPng; }
}
[Obsolete("Use GetImage instead.")]
public Image Image
{
#if (!KeePassLibSD && !KeePassUAP)
get { return GetImage(16, 16); } // Backward compatibility
#else
get { return GetImage(); } // Backward compatibility
#endif
}
public PwCustomIcon(PwUuid pwUuid, byte[] pbImageDataPng)
{
Debug.Assert(pwUuid != null);
if(pwUuid == null) throw new ArgumentNullException("pwUuid");
Debug.Assert(!pwUuid.Equals(PwUuid.Zero));
if(pwUuid.Equals(PwUuid.Zero)) throw new ArgumentException("pwUuid == 0.");
Debug.Assert(pbImageDataPng != null);
if(pbImageDataPng == null) throw new ArgumentNullException("pbImageDataPng");
m_pwUuid = pwUuid;
m_pbImageDataPng = pbImageDataPng;
// MemoryStream ms = new MemoryStream(m_pbImageDataPng, false);
// m_imgOrg = Image.FromStream(ms);
// ms.Close();
try { m_imgOrg = GfxUtil.LoadImage(m_pbImageDataPng); }
catch(Exception) { Debug.Assert(false); m_imgOrg = null; }
if(m_imgOrg != null)
m_dImageCache[GetID((int)m_imgOrg.Width, (int)m_imgOrg.Height)] =
m_imgOrg;
}
private static long GetID(int w, int h)
{
return (((long)w << 32) ^ (long)h);
}
/// <summary>
/// Get the icon as an <c>Image</c> (original size).
/// </summary>
public Image GetImage()
{
return m_imgOrg;
}
#if (!KeePassLibSD && !KeePassUAP)
/// <summary>
/// Get the icon as an <c>Image</c> (with the specified size).
/// </summary>
/// <param name="w">Width of the returned image.</param>
/// <param name="h">Height of the returned image.</param>
public Image GetImage(int w, int h)
{
if(w < 0) { Debug.Assert(false); return m_imgOrg; }
if(h < 0) { Debug.Assert(false); return m_imgOrg; }
if(m_imgOrg == null) return null;
long lID = GetID(w, h);
Image img;
if(m_dImageCache.TryGetValue(lID, out img)) return img;
img = GfxUtil.ScaleImage(m_imgOrg, w, h, ScaleTransformFlags.UIIcon);
m_dImageCache[lID] = img;
return img;
}
#endif
}
}

File diff suppressed because it is too large Load Diff

517
ModernKeePassLib/PwDefs.cs Normal file
View File

@@ -0,0 +1,517 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2019 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Xml.Serialization;
using ModernKeePassLib.Delegates;
using ModernKeePassLib.Interfaces;
using ModernKeePassLib.Serialization;
namespace ModernKeePassLib
{
/// <summary>
/// Contains KeePassLib-global definitions and enums.
/// </summary>
public static class PwDefs
{
/// <summary>
/// The product name.
/// </summary>
public static readonly string ProductName = "KeePass Password Safe";
/// <summary>
/// A short, simple string representing the product name. The string
/// should contain no spaces, directory separator characters, etc.
/// </summary>
public static readonly string ShortProductName = "KeePass";
internal const string UnixName = "keepass2";
internal const string ResClass = "KeePass2"; // With initial capital
/// <summary>
/// Version, encoded as 32-bit unsigned integer.
/// 2.00 = 0x02000000, 2.01 = 0x02000100, ..., 2.18 = 0x02010800.
/// As of 2.19, the version is encoded component-wise per byte,
/// e.g. 2.19 = 0x02130000.
/// It is highly recommended to use <c>FileVersion64</c> instead.
/// </summary>
public static readonly uint Version32 = 0x02290000;
/// <summary>
/// Version, encoded as 64-bit unsigned integer
/// (component-wise, 16 bits per component).
/// </summary>
public static readonly ulong FileVersion64 = 0x0002002900000000UL;
/// <summary>
/// Version, encoded as string.
/// </summary>
public static readonly string VersionString = "2.41";
public static readonly string Copyright = @"Copyright © 2003-2019 Dominik Reichl";
/// <summary>
/// Product website URL. Terminated by a forward slash.
/// </summary>
public static readonly string HomepageUrl = "https://keepass.info/";
/// <summary>
/// URL to the online translations page.
/// </summary>
public static readonly string TranslationsUrl = "https://keepass.info/translations.html";
/// <summary>
/// URL to the online plugins page.
/// </summary>
public static readonly string PluginsUrl = "https://keepass.info/plugins.html";
/// <summary>
/// Product donations URL.
/// </summary>
public static readonly string DonationsUrl = "https://keepass.info/donate.html";
/// <summary>
/// URL to the root path of the online KeePass help. Terminated by
/// a forward slash.
/// </summary>
public static readonly string HelpUrl = "https://keepass.info/help/";
/// <summary>
/// URL to a TXT file (eventually compressed) that contains information
/// about the latest KeePass version available on the website.
/// </summary>
public static readonly string VersionUrl = "https://www.dominik-reichl.de/update/version2x.txt.gz";
/// <summary>
/// A <c>DateTime</c> object that represents the time when the assembly
/// was loaded.
/// </summary>
public static readonly DateTime DtDefaultNow = DateTime.UtcNow;
/// <summary>
/// Default number of master key encryption/transformation rounds
/// (making dictionary attacks harder).
/// </summary>
public static readonly ulong DefaultKeyEncryptionRounds = 60000;
/// <summary>
/// Default identifier string for the title field.
/// Should not contain spaces, tabs or other whitespace.
/// </summary>
public const string TitleField = "Title";
// Const instead of static readonly for backward compatibility with plugins
/// <summary>
/// Default identifier string for the user name field.
/// Should not contain spaces, tabs or other whitespace.
/// </summary>
public const string UserNameField = "UserName";
// Const instead of static readonly for backward compatibility with plugins
/// <summary>
/// Default identifier string for the password field.
/// Should not contain spaces, tabs or other whitespace.
/// </summary>
public const string PasswordField = "Password";
// Const instead of static readonly for backward compatibility with plugins
/// <summary>
/// Default identifier string for the URL field.
/// Should not contain spaces, tabs or other whitespace.
/// </summary>
public const string UrlField = "URL";
// Const instead of static readonly for backward compatibility with plugins
/// <summary>
/// Default identifier string for the notes field.
/// Should not contain spaces, tabs or other whitespace.
/// </summary>
public const string NotesField = "Notes";
// Const instead of static readonly for backward compatibility with plugins
/// <summary>
/// Default identifier string for the field which will contain TAN indices.
/// </summary>
public static readonly string TanIndexField = UserNameField;
/// <summary>
/// Default title of an entry that is really a TAN entry.
/// </summary>
public static readonly string TanTitle = @"<TAN>";
/// <summary>
/// Prefix of a custom auto-type string field.
/// </summary>
public static readonly string AutoTypeStringPrefix = "S:";
/// <summary>
/// Default string representing a hidden password.
/// </summary>
public static readonly string HiddenPassword = "********";
/// <summary>
/// Default auto-type keystroke sequence. If no custom sequence is
/// specified, this sequence is used.
/// </summary>
public static readonly string DefaultAutoTypeSequence = @"{USERNAME}{TAB}{PASSWORD}{ENTER}";
/// <summary>
/// Default auto-type keystroke sequence for TAN entries. If no custom
/// sequence is specified, this sequence is used.
/// </summary>
public static readonly string DefaultAutoTypeSequenceTan = @"{PASSWORD}";
/// <summary>
/// Check if a name is a standard field name.
/// </summary>
/// <param name="strFieldName">Input field name.</param>
/// <returns>Returns <c>true</c>, if the field name is a standard
/// field name (title, user name, password, ...), otherwise <c>false</c>.</returns>
public static bool IsStandardField(string strFieldName)
{
Debug.Assert(strFieldName != null); if(strFieldName == null) return false;
if(strFieldName.Equals(TitleField)) return true;
if(strFieldName.Equals(UserNameField)) return true;
if(strFieldName.Equals(PasswordField)) return true;
if(strFieldName.Equals(UrlField)) return true;
if(strFieldName.Equals(NotesField)) return true;
return false;
}
public static List<string> GetStandardFields()
{
List<string> l = new List<string>();
l.Add(TitleField);
l.Add(UserNameField);
l.Add(PasswordField);
l.Add(UrlField);
l.Add(NotesField);
return l;
}
/// <summary>
/// Check whether an entry is a TAN entry.
/// </summary>
public static bool IsTanEntry(PwEntry pe)
{
if(pe == null) { Debug.Assert(false); return false; }
return (pe.Strings.ReadSafe(PwDefs.TitleField) == TanTitle);
}
internal static string GetTranslationDisplayVersion(string strFileVersion)
{
if(strFileVersion == null) { Debug.Assert(false); return string.Empty; }
if(strFileVersion == "2.39") return "2.39 / 2.39.1";
return strFileVersion;
}
}
// #pragma warning disable 1591 // Missing XML comments warning
/// <summary>
/// Search parameters for group and entry searches.
/// </summary>
public sealed class SearchParameters
{
private string m_strText = string.Empty;
[DefaultValue("")]
public string SearchString
{
get { return m_strText; }
set
{
if(value == null) throw new ArgumentNullException("value");
m_strText = value;
}
}
private bool m_bRegex = false;
[DefaultValue(false)]
public bool RegularExpression
{
get { return m_bRegex; }
set { m_bRegex = value; }
}
private bool m_bSearchInTitles = true;
[DefaultValue(true)]
public bool SearchInTitles
{
get { return m_bSearchInTitles; }
set { m_bSearchInTitles = value; }
}
private bool m_bSearchInUserNames = true;
[DefaultValue(true)]
public bool SearchInUserNames
{
get { return m_bSearchInUserNames; }
set { m_bSearchInUserNames = value; }
}
private bool m_bSearchInPasswords = false;
[DefaultValue(false)]
public bool SearchInPasswords
{
get { return m_bSearchInPasswords; }
set { m_bSearchInPasswords = value; }
}
private bool m_bSearchInUrls = true;
[DefaultValue(true)]
public bool SearchInUrls
{
get { return m_bSearchInUrls; }
set { m_bSearchInUrls = value; }
}
private bool m_bSearchInNotes = true;
[DefaultValue(true)]
public bool SearchInNotes
{
get { return m_bSearchInNotes; }
set { m_bSearchInNotes = value; }
}
private bool m_bSearchInOther = true;
[DefaultValue(true)]
public bool SearchInOther
{
get { return m_bSearchInOther; }
set { m_bSearchInOther = value; }
}
private bool m_bSearchInStringNames = false;
[DefaultValue(false)]
public bool SearchInStringNames
{
get { return m_bSearchInStringNames; }
set { m_bSearchInStringNames = value; }
}
private bool m_bSearchInTags = true;
[DefaultValue(true)]
public bool SearchInTags
{
get { return m_bSearchInTags; }
set { m_bSearchInTags = value; }
}
private bool m_bSearchInUuids = false;
[DefaultValue(false)]
public bool SearchInUuids
{
get { return m_bSearchInUuids; }
set { m_bSearchInUuids = value; }
}
private bool m_bSearchInGroupPaths = false;
[DefaultValue(false)]
public bool SearchInGroupPaths
{
get { return m_bSearchInGroupPaths; }
set { m_bSearchInGroupPaths = value; }
}
private bool m_bSearchInGroupNames = false;
[DefaultValue(false)]
public bool SearchInGroupNames
{
get { return m_bSearchInGroupNames; }
set { m_bSearchInGroupNames = value; }
}
#if ModernKeePassLib || KeePassUAP
private StringComparison m_scType = StringComparison.OrdinalIgnoreCase;
#else
private StringComparison m_scType = StringComparison.InvariantCultureIgnoreCase;
#endif
/// <summary>
/// String comparison type. Specifies the condition when the specified
/// text matches a group/entry string.
/// </summary>
public StringComparison ComparisonMode
{
get { return m_scType; }
set { m_scType = value; }
}
private bool m_bExcludeExpired = false;
[DefaultValue(false)]
public bool ExcludeExpired
{
get { return m_bExcludeExpired; }
set { m_bExcludeExpired = value; }
}
private bool m_bRespectEntrySearchingDisabled = true;
[DefaultValue(true)]
public bool RespectEntrySearchingDisabled
{
get { return m_bRespectEntrySearchingDisabled; }
set { m_bRespectEntrySearchingDisabled = value; }
}
private StrPwEntryDelegate m_fnDataTrf = null;
[XmlIgnore]
public StrPwEntryDelegate DataTransformationFn
{
get { return m_fnDataTrf; }
set { m_fnDataTrf = value; }
}
private string m_strDataTrf = string.Empty;
/// <summary>
/// Only for serialization.
/// </summary>
[DefaultValue("")]
public string DataTransformation
{
get { return m_strDataTrf; }
set
{
if(value == null) throw new ArgumentNullException("value");
m_strDataTrf = value;
}
}
[XmlIgnore]
public static SearchParameters None
{
get
{
SearchParameters sp = new SearchParameters();
Debug.Assert(sp.m_strText.Length == 0);
Debug.Assert(!sp.m_bRegex);
sp.m_bSearchInTitles = false;
sp.m_bSearchInUserNames = false;
Debug.Assert(!sp.m_bSearchInPasswords);
sp.m_bSearchInUrls = false;
sp.m_bSearchInNotes = false;
sp.m_bSearchInOther = false;
Debug.Assert(!sp.m_bSearchInStringNames);
sp.m_bSearchInTags = false;
Debug.Assert(!sp.m_bSearchInUuids);
Debug.Assert(!sp.m_bSearchInGroupPaths);
Debug.Assert(!sp.m_bSearchInGroupNames);
// Debug.Assert(sp.m_scType == StringComparison.InvariantCultureIgnoreCase);
Debug.Assert(!sp.m_bExcludeExpired);
Debug.Assert(sp.m_bRespectEntrySearchingDisabled);
return sp;
}
}
/// <summary>
/// Construct a new search parameters object.
/// </summary>
public SearchParameters()
{
}
public SearchParameters Clone()
{
return (SearchParameters)this.MemberwiseClone();
}
}
// #pragma warning restore 1591 // Missing XML comments warning
// #pragma warning disable 1591 // Missing XML comments warning
/// <summary>
/// Memory protection configuration structure (for default fields).
/// </summary>
public sealed class MemoryProtectionConfig : IDeepCloneable<MemoryProtectionConfig>
{
public bool ProtectTitle = false;
public bool ProtectUserName = false;
public bool ProtectPassword = true;
public bool ProtectUrl = false;
public bool ProtectNotes = false;
// public bool AutoEnableVisualHiding = false;
public MemoryProtectionConfig CloneDeep()
{
return (MemoryProtectionConfig)this.MemberwiseClone();
}
public bool GetProtection(string strField)
{
if(strField == PwDefs.TitleField) return this.ProtectTitle;
if(strField == PwDefs.UserNameField) return this.ProtectUserName;
if(strField == PwDefs.PasswordField) return this.ProtectPassword;
if(strField == PwDefs.UrlField) return this.ProtectUrl;
if(strField == PwDefs.NotesField) return this.ProtectNotes;
return false;
}
}
// #pragma warning restore 1591 // Missing XML comments warning
public sealed class ObjectTouchedEventArgs : EventArgs
{
private object m_o;
public object Object { get { return m_o; } }
private bool m_bModified;
public bool Modified { get { return m_bModified; } }
private bool m_bParentsTouched;
public bool ParentsTouched { get { return m_bParentsTouched; } }
public ObjectTouchedEventArgs(object o, bool bModified,
bool bParentsTouched)
{
m_o = o;
m_bModified = bModified;
m_bParentsTouched = bParentsTouched;
}
}
public sealed class IOAccessEventArgs : EventArgs
{
private IOConnectionInfo m_ioc;
public IOConnectionInfo IOConnectionInfo { get { return m_ioc; } }
private IOConnectionInfo m_ioc2;
public IOConnectionInfo IOConnectionInfo2 { get { return m_ioc2; } }
private IOAccessType m_t;
public IOAccessType Type { get { return m_t; } }
public IOAccessEventArgs(IOConnectionInfo ioc, IOConnectionInfo ioc2,
IOAccessType t)
{
m_ioc = ioc;
m_ioc2 = ioc2;
m_t = t;
}
}
}

View File

@@ -0,0 +1,86 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2019 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
using System;
using System.Collections.Generic;
using System.Diagnostics;
using ModernKeePassLib.Interfaces;
namespace ModernKeePassLib
{
/// <summary>
/// Represents an object that has been deleted.
/// </summary>
public sealed class PwDeletedObject : IDeepCloneable<PwDeletedObject>
{
private PwUuid m_uuid = PwUuid.Zero;
/// <summary>
/// UUID of the entry that has been deleted.
/// </summary>
public PwUuid Uuid
{
get { return m_uuid; }
set
{
if(value == null) throw new ArgumentNullException("value");
m_uuid = value;
}
}
private DateTime m_dtDeletionTime = PwDefs.DtDefaultNow;
/// <summary>
/// The date/time when the entry has been deleted.
/// </summary>
public DateTime DeletionTime
{
get { return m_dtDeletionTime; }
set { m_dtDeletionTime = value; }
}
/// <summary>
/// Construct a new <c>PwDeletedObject</c> object.
/// </summary>
public PwDeletedObject()
{
}
public PwDeletedObject(PwUuid uuid, DateTime dtDeletionTime)
{
if(uuid == null) throw new ArgumentNullException("uuid");
m_uuid = uuid;
m_dtDeletionTime = dtDeletionTime;
}
/// <summary>
/// Clone the object.
/// </summary>
/// <returns>Value copy of the current object.</returns>
public PwDeletedObject CloneDeep()
{
PwDeletedObject pdo = new PwDeletedObject();
pdo.m_uuid = m_uuid; // PwUuid objects are immutable
pdo.m_dtDeletionTime = m_dtDeletionTime;
return pdo;
}
}
}

947
ModernKeePassLib/PwEntry.cs Normal file
View File

@@ -0,0 +1,947 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2019 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using ModernKeePassLib.Collections;
using ModernKeePassLib.Interfaces;
using ModernKeePassLib.Security;
using ModernKeePassLib.Utility;
namespace ModernKeePassLib
{
/// <summary>
/// A class representing a password entry. A password entry consists of several
/// fields like title, user name, password, etc. Each password entry has a
/// unique ID (UUID).
/// </summary>
public sealed class PwEntry : ITimeLogger, IStructureItem, IDeepCloneable<PwEntry>
{
private PwUuid m_uuid = PwUuid.Zero;
private PwGroup m_pParentGroup = null;
private DateTime m_tParentGroupLastMod = PwDefs.DtDefaultNow;
private ProtectedStringDictionary m_listStrings = new ProtectedStringDictionary();
private ProtectedBinaryDictionary m_listBinaries = new ProtectedBinaryDictionary();
private AutoTypeConfig m_listAutoType = new AutoTypeConfig();
private PwObjectList<PwEntry> m_listHistory = new PwObjectList<PwEntry>();
private PwIcon m_pwIcon = PwIcon.Key;
private PwUuid m_pwCustomIconID = PwUuid.Zero;
private Color m_clrForeground = Color.Empty;
private Color m_clrBackground = Color.Empty;
private DateTime m_tCreation = PwDefs.DtDefaultNow;
private DateTime m_tLastMod = PwDefs.DtDefaultNow;
private DateTime m_tLastAccess = PwDefs.DtDefaultNow;
private DateTime m_tExpire = PwDefs.DtDefaultNow;
private bool m_bExpires = false;
private ulong m_uUsageCount = 0;
private string m_strOverrideUrl = string.Empty;
private List<string> m_vTags = new List<string>();
private StringDictionaryEx m_dCustomData = new StringDictionaryEx();
/// <summary>
/// UUID of this entry.
/// </summary>
public PwUuid Uuid
{
get { return m_uuid; }
set
{
Debug.Assert(value != null); if(value == null) throw new ArgumentNullException("value");
m_uuid = value;
}
}
/// <summary>
/// Reference to a group which contains the current entry.
/// </summary>
public PwGroup ParentGroup
{
get { return m_pParentGroup; }
// Plugins: use <c>PwGroup.AddEntry</c> instead.
internal set { m_pParentGroup = value; }
}
/// <summary>
/// The date/time when the location of the object was last changed.
/// </summary>
public DateTime LocationChanged
{
get { return m_tParentGroupLastMod; }
set { m_tParentGroupLastMod = value; }
}
/// <summary>
/// Get or set all entry strings.
/// </summary>
public ProtectedStringDictionary Strings
{
get { return m_listStrings; }
set
{
Debug.Assert(value != null); if(value == null) throw new ArgumentNullException("value");
m_listStrings = value;
}
}
/// <summary>
/// Get or set all entry binaries.
/// </summary>
public ProtectedBinaryDictionary Binaries
{
get { return m_listBinaries; }
set
{
Debug.Assert(value != null); if(value == null) throw new ArgumentNullException("value");
m_listBinaries = value;
}
}
/// <summary>
/// Get or set all auto-type window/keystroke sequence associations.
/// </summary>
public AutoTypeConfig AutoType
{
get { return m_listAutoType; }
set
{
Debug.Assert(value != null); if(value == null) throw new ArgumentNullException("value");
m_listAutoType = value;
}
}
/// <summary>
/// Get all previous versions of this entry (backups).
/// </summary>
public PwObjectList<PwEntry> History
{
get { return m_listHistory; }
set
{
Debug.Assert(value != null); if(value == null) throw new ArgumentNullException("value");
m_listHistory = value;
}
}
/// <summary>
/// Image ID specifying the icon that will be used for this entry.
/// </summary>
public PwIcon IconId
{
get { return m_pwIcon; }
set { m_pwIcon = value; }
}
/// <summary>
/// Get the custom icon ID. This value is 0, if no custom icon is
/// being used (i.e. the icon specified by the <c>IconID</c> property
/// should be displayed).
/// </summary>
public PwUuid CustomIconUuid
{
get { return m_pwCustomIconID; }
set
{
Debug.Assert(value != null); if(value == null) throw new ArgumentNullException("value");
m_pwCustomIconID = value;
}
}
/// <summary>
/// Get or set the foreground color of this entry.
/// </summary>
public Color ForegroundColor
{
get { return m_clrForeground; }
set { m_clrForeground = value; }
}
/// <summary>
/// Get or set the background color of this entry.
/// </summary>
public Color BackgroundColor
{
get { return m_clrBackground; }
set { m_clrBackground = value; }
}
/// <summary>
/// The date/time when this entry was created.
/// </summary>
public DateTime CreationTime
{
get { return m_tCreation; }
set { m_tCreation = value; }
}
/// <summary>
/// The date/time when this entry was last modified.
/// </summary>
public DateTime LastModificationTime
{
get { return m_tLastMod; }
set { m_tLastMod = value; }
}
/// <summary>
/// The date/time when this entry was last accessed (read).
/// </summary>
public DateTime LastAccessTime
{
get { return m_tLastAccess; }
set { m_tLastAccess = value; }
}
/// <summary>
/// The date/time when this entry expires. Use the <c>Expires</c> property
/// to specify if the entry does actually expire or not.
/// </summary>
public DateTime ExpiryTime
{
get { return m_tExpire; }
set { m_tExpire = value; }
}
/// <summary>
/// Specifies whether the entry expires or not.
/// </summary>
public bool Expires
{
get { return m_bExpires; }
set { m_bExpires = value; }
}
/// <summary>
/// Get or set the usage count of the entry. To increase the usage
/// count by one, use the <c>Touch</c> function.
/// </summary>
public ulong UsageCount
{
get { return m_uUsageCount; }
set { m_uUsageCount = value; }
}
/// <summary>
/// Entry-specific override URL. If this string is non-empty,
/// </summary>
public string OverrideUrl
{
get { return m_strOverrideUrl; }
set
{
if(value == null) throw new ArgumentNullException("value");
m_strOverrideUrl = value;
}
}
/// <summary>
/// List of tags associated with this entry.
/// </summary>
public List<string> Tags
{
get { return m_vTags; }
set
{
if(value == null) throw new ArgumentNullException("value");
m_vTags = value;
}
}
/// <summary>
/// Custom data container that can be used by plugins to store
/// own data in KeePass entries.
/// The data is stored in the encrypted part of encrypted
/// database files.
/// Use unique names for your items, e.g. "PluginName_ItemName".
/// </summary>
public StringDictionaryEx CustomData
{
get { return m_dCustomData; }
internal set
{
if(value == null) { Debug.Assert(false); throw new ArgumentNullException("value"); }
m_dCustomData = value;
}
}
public static EventHandler<ObjectTouchedEventArgs> EntryTouched;
public EventHandler<ObjectTouchedEventArgs> Touched;
/// <summary>
/// Construct a new, empty password entry. Member variables will be initialized
/// to their default values.
/// </summary>
/// <param name="bCreateNewUuid">If <c>true</c>, a new UUID will be created
/// for this entry. If <c>false</c>, the UUID is zero and you must set it
/// manually later.</param>
/// <param name="bSetTimes">If <c>true</c>, the creation, last modification
/// and last access times will be set to the current system time.</param>
public PwEntry(bool bCreateNewUuid, bool bSetTimes)
{
if(bCreateNewUuid) m_uuid = new PwUuid(true);
if(bSetTimes)
{
DateTime dtNow = DateTime.UtcNow;
m_tCreation = dtNow;
m_tLastMod = dtNow;
m_tLastAccess = dtNow;
m_tParentGroupLastMod = dtNow;
}
}
/// <summary>
/// Construct a new, empty password entry. Member variables will be initialized
/// to their default values.
/// </summary>
/// <param name="pwParentGroup">Reference to the containing group, this
/// parameter may be <c>null</c> and set later manually.</param>
/// <param name="bCreateNewUuid">If <c>true</c>, a new UUID will be created
/// for this entry. If <c>false</c>, the UUID is zero and you must set it
/// manually later.</param>
/// <param name="bSetTimes">If <c>true</c>, the creation, last modification
/// and last access times will be set to the current system time.</param>
[Obsolete("Use a different constructor. To add an entry to a group, use AddEntry of PwGroup.")]
public PwEntry(PwGroup pwParentGroup, bool bCreateNewUuid, bool bSetTimes)
{
m_pParentGroup = pwParentGroup;
if(bCreateNewUuid) m_uuid = new PwUuid(true);
if(bSetTimes)
{
DateTime dtNow = DateTime.UtcNow;
m_tCreation = dtNow;
m_tLastMod = dtNow;
m_tLastAccess = dtNow;
m_tParentGroupLastMod = dtNow;
}
}
#if DEBUG
// For display in debugger
public override string ToString()
{
return (@"PwEntry '" + m_listStrings.ReadSafe(PwDefs.TitleField) + @"'");
}
#endif
/// <summary>
/// Clone the current entry. The returned entry is an exact value copy
/// of the current entry (including UUID and parent group reference).
/// All mutable members are cloned.
/// </summary>
/// <returns>Exact value clone. All references to mutable values changed.</returns>
public PwEntry CloneDeep()
{
PwEntry peNew = new PwEntry(false, false);
peNew.m_uuid = m_uuid; // PwUuid is immutable
peNew.m_pParentGroup = m_pParentGroup;
peNew.m_tParentGroupLastMod = m_tParentGroupLastMod;
peNew.m_listStrings = m_listStrings.CloneDeep();
peNew.m_listBinaries = m_listBinaries.CloneDeep();
peNew.m_listAutoType = m_listAutoType.CloneDeep();
peNew.m_listHistory = m_listHistory.CloneDeep();
peNew.m_pwIcon = m_pwIcon;
peNew.m_pwCustomIconID = m_pwCustomIconID;
peNew.m_clrForeground = m_clrForeground;
peNew.m_clrBackground = m_clrBackground;
peNew.m_tCreation = m_tCreation;
peNew.m_tLastMod = m_tLastMod;
peNew.m_tLastAccess = m_tLastAccess;
peNew.m_tExpire = m_tExpire;
peNew.m_bExpires = m_bExpires;
peNew.m_uUsageCount = m_uUsageCount;
peNew.m_strOverrideUrl = m_strOverrideUrl;
peNew.m_vTags = new List<string>(m_vTags);
peNew.m_dCustomData = m_dCustomData.CloneDeep();
return peNew;
}
public PwEntry CloneStructure()
{
PwEntry peNew = new PwEntry(false, false);
peNew.m_uuid = m_uuid; // PwUuid is immutable
peNew.m_tParentGroupLastMod = m_tParentGroupLastMod;
// Do not assign m_pParentGroup
return peNew;
}
private static PwCompareOptions BuildCmpOpt(bool bIgnoreParentGroup,
bool bIgnoreLastMod, bool bIgnoreLastAccess, bool bIgnoreHistory,
bool bIgnoreThisLastBackup)
{
PwCompareOptions pwOpt = PwCompareOptions.None;
if(bIgnoreParentGroup) pwOpt |= PwCompareOptions.IgnoreParentGroup;
if(bIgnoreLastMod) pwOpt |= PwCompareOptions.IgnoreLastMod;
if(bIgnoreLastAccess) pwOpt |= PwCompareOptions.IgnoreLastAccess;
if(bIgnoreHistory) pwOpt |= PwCompareOptions.IgnoreHistory;
if(bIgnoreThisLastBackup) pwOpt |= PwCompareOptions.IgnoreLastBackup;
return pwOpt;
}
[Obsolete]
public bool EqualsEntry(PwEntry pe, bool bIgnoreParentGroup, bool bIgnoreLastMod,
bool bIgnoreLastAccess, bool bIgnoreHistory, bool bIgnoreThisLastBackup)
{
return EqualsEntry(pe, BuildCmpOpt(bIgnoreParentGroup, bIgnoreLastMod,
bIgnoreLastAccess, bIgnoreHistory, bIgnoreThisLastBackup),
MemProtCmpMode.None);
}
[Obsolete]
public bool EqualsEntry(PwEntry pe, bool bIgnoreParentGroup, bool bIgnoreLastMod,
bool bIgnoreLastAccess, bool bIgnoreHistory, bool bIgnoreThisLastBackup,
MemProtCmpMode mpCmpStr)
{
return EqualsEntry(pe, BuildCmpOpt(bIgnoreParentGroup, bIgnoreLastMod,
bIgnoreLastAccess, bIgnoreHistory, bIgnoreThisLastBackup), mpCmpStr);
}
public bool EqualsEntry(PwEntry pe, PwCompareOptions pwOpt,
MemProtCmpMode mpCmpStr)
{
if(pe == null) { Debug.Assert(false); return false; }
bool bNeEqStd = ((pwOpt & PwCompareOptions.NullEmptyEquivStd) !=
PwCompareOptions.None);
bool bIgnoreLastAccess = ((pwOpt & PwCompareOptions.IgnoreLastAccess) !=
PwCompareOptions.None);
bool bIgnoreLastMod = ((pwOpt & PwCompareOptions.IgnoreLastMod) !=
PwCompareOptions.None);
if(!m_uuid.Equals(pe.m_uuid)) return false;
if((pwOpt & PwCompareOptions.IgnoreParentGroup) == PwCompareOptions.None)
{
if(m_pParentGroup != pe.m_pParentGroup) return false;
if(!bIgnoreLastMod && (m_tParentGroupLastMod != pe.m_tParentGroupLastMod))
return false;
}
if(!m_listStrings.EqualsDictionary(pe.m_listStrings, pwOpt, mpCmpStr))
return false;
if(!m_listBinaries.EqualsDictionary(pe.m_listBinaries)) return false;
if(!m_listAutoType.Equals(pe.m_listAutoType)) return false;
if((pwOpt & PwCompareOptions.IgnoreHistory) == PwCompareOptions.None)
{
bool bIgnoreLastBackup = ((pwOpt & PwCompareOptions.IgnoreLastBackup) !=
PwCompareOptions.None);
if(!bIgnoreLastBackup && (m_listHistory.UCount != pe.m_listHistory.UCount))
return false;
if(bIgnoreLastBackup && (m_listHistory.UCount == 0))
{
Debug.Assert(false);
return false;
}
if(bIgnoreLastBackup && ((m_listHistory.UCount - 1) != pe.m_listHistory.UCount))
return false;
PwCompareOptions cmpSub = PwCompareOptions.IgnoreParentGroup;
if(bNeEqStd) cmpSub |= PwCompareOptions.NullEmptyEquivStd;
if(bIgnoreLastMod) cmpSub |= PwCompareOptions.IgnoreLastMod;
if(bIgnoreLastAccess) cmpSub |= PwCompareOptions.IgnoreLastAccess;
for(uint uHist = 0; uHist < pe.m_listHistory.UCount; ++uHist)
{
if(!m_listHistory.GetAt(uHist).EqualsEntry(pe.m_listHistory.GetAt(
uHist), cmpSub, MemProtCmpMode.None))
return false;
}
}
if(m_pwIcon != pe.m_pwIcon) return false;
if(!m_pwCustomIconID.Equals(pe.m_pwCustomIconID)) return false;
if(m_clrForeground != pe.m_clrForeground) return false;
if(m_clrBackground != pe.m_clrBackground) return false;
if(m_tCreation != pe.m_tCreation) return false;
if(!bIgnoreLastMod && (m_tLastMod != pe.m_tLastMod)) return false;
if(!bIgnoreLastAccess && (m_tLastAccess != pe.m_tLastAccess)) return false;
if(m_tExpire != pe.m_tExpire) return false;
if(m_bExpires != pe.m_bExpires) return false;
if(!bIgnoreLastAccess && (m_uUsageCount != pe.m_uUsageCount)) return false;
if(m_strOverrideUrl != pe.m_strOverrideUrl) return false;
if(m_vTags.Count != pe.m_vTags.Count) return false;
for(int iTag = 0; iTag < m_vTags.Count; ++iTag)
{
if(m_vTags[iTag] != pe.m_vTags[iTag]) return false;
}
if(!m_dCustomData.Equals(pe.m_dCustomData)) return false;
return true;
}
/// <summary>
/// Assign properties to the current entry based on a template entry.
/// </summary>
/// <param name="peTemplate">Template entry. Must not be <c>null</c>.</param>
/// <param name="bOnlyIfNewer">Only set the properties of the template entry
/// if it is newer than the current one.</param>
/// <param name="bIncludeHistory">If <c>true</c>, the history will be
/// copied, too.</param>
/// <param name="bAssignLocationChanged">If <c>true</c>, the
/// <c>LocationChanged</c> property is copied, otherwise not.</param>
public void AssignProperties(PwEntry peTemplate, bool bOnlyIfNewer,
bool bIncludeHistory, bool bAssignLocationChanged)
{
if(peTemplate == null) { Debug.Assert(false); throw new ArgumentNullException("peTemplate"); }
if(bOnlyIfNewer && (TimeUtil.Compare(peTemplate.m_tLastMod,
m_tLastMod, true) < 0))
return;
// Template UUID should be the same as the current one
Debug.Assert(m_uuid.Equals(peTemplate.m_uuid));
m_uuid = peTemplate.m_uuid;
if(bAssignLocationChanged)
m_tParentGroupLastMod = peTemplate.m_tParentGroupLastMod;
m_listStrings = peTemplate.m_listStrings.CloneDeep();
m_listBinaries = peTemplate.m_listBinaries.CloneDeep();
m_listAutoType = peTemplate.m_listAutoType.CloneDeep();
if(bIncludeHistory)
m_listHistory = peTemplate.m_listHistory.CloneDeep();
m_pwIcon = peTemplate.m_pwIcon;
m_pwCustomIconID = peTemplate.m_pwCustomIconID; // Immutable
m_clrForeground = peTemplate.m_clrForeground;
m_clrBackground = peTemplate.m_clrBackground;
m_tCreation = peTemplate.m_tCreation;
m_tLastMod = peTemplate.m_tLastMod;
m_tLastAccess = peTemplate.m_tLastAccess;
m_tExpire = peTemplate.m_tExpire;
m_bExpires = peTemplate.m_bExpires;
m_uUsageCount = peTemplate.m_uUsageCount;
m_strOverrideUrl = peTemplate.m_strOverrideUrl;
m_vTags = new List<string>(peTemplate.m_vTags);
m_dCustomData = peTemplate.m_dCustomData.CloneDeep();
}
/// <summary>
/// Touch the entry. This function updates the internal last access
/// time. If the <paramref name="bModified" /> parameter is <c>true</c>,
/// the last modification time gets updated, too.
/// </summary>
/// <param name="bModified">Modify last modification time.</param>
public void Touch(bool bModified)
{
Touch(bModified, true);
}
/// <summary>
/// Touch the entry. This function updates the internal last access
/// time. If the <paramref name="bModified" /> parameter is <c>true</c>,
/// the last modification time gets updated, too.
/// </summary>
/// <param name="bModified">Modify last modification time.</param>
/// <param name="bTouchParents">If <c>true</c>, all parent objects
/// get touched, too.</param>
public void Touch(bool bModified, bool bTouchParents)
{
m_tLastAccess = DateTime.UtcNow;
++m_uUsageCount;
if(bModified) m_tLastMod = m_tLastAccess;
if(this.Touched != null)
this.Touched(this, new ObjectTouchedEventArgs(this,
bModified, bTouchParents));
if(PwEntry.EntryTouched != null)
PwEntry.EntryTouched(this, new ObjectTouchedEventArgs(this,
bModified, bTouchParents));
if(bTouchParents && (m_pParentGroup != null))
m_pParentGroup.Touch(bModified, true);
}
/// <summary>
/// Create a backup of this entry. The backup item doesn't contain any
/// history items.
/// </summary>
[Obsolete]
public void CreateBackup()
{
CreateBackup(null);
}
/// <summary>
/// Create a backup of this entry. The backup item doesn't contain any
/// history items.
/// <param name="pwHistMntcSettings">If this parameter isn't <c>null</c>,
/// the history list is maintained automatically (i.e. old backups are
/// deleted if there are too many or the history size is too large).
/// This parameter may be <c>null</c> (no maintenance then).</param>
/// </summary>
public void CreateBackup(PwDatabase pwHistMntcSettings)
{
PwEntry peCopy = CloneDeep();
peCopy.History = new PwObjectList<PwEntry>(); // Remove history
m_listHistory.Add(peCopy); // Must be added at end, see EqualsEntry
if(pwHistMntcSettings != null) MaintainBackups(pwHistMntcSettings);
}
/// <summary>
/// Restore an entry snapshot from backups.
/// </summary>
/// <param name="uBackupIndex">Index of the backup item, to which
/// should be reverted.</param>
[Obsolete]
public void RestoreFromBackup(uint uBackupIndex)
{
RestoreFromBackup(uBackupIndex, null);
}
/// <summary>
/// Restore an entry snapshot from backups.
/// </summary>
/// <param name="uBackupIndex">Index of the backup item, to which
/// should be reverted.</param>
/// <param name="pwHistMntcSettings">If this parameter isn't <c>null</c>,
/// the history list is maintained automatically (i.e. old backups are
/// deleted if there are too many or the history size is too large).
/// This parameter may be <c>null</c> (no maintenance then).</param>
public void RestoreFromBackup(uint uBackupIndex, PwDatabase pwHistMntcSettings)
{
Debug.Assert(uBackupIndex < m_listHistory.UCount);
if(uBackupIndex >= m_listHistory.UCount)
throw new ArgumentOutOfRangeException("uBackupIndex");
PwEntry pe = m_listHistory.GetAt(uBackupIndex);
Debug.Assert(pe != null); if(pe == null) throw new InvalidOperationException();
CreateBackup(pwHistMntcSettings); // Backup current data before restoring
AssignProperties(pe, false, false, false);
}
public bool HasBackupOfData(PwEntry peData, bool bIgnoreLastMod,
bool bIgnoreLastAccess)
{
if(peData == null) { Debug.Assert(false); return false; }
PwCompareOptions cmpOpt = (PwCompareOptions.IgnoreParentGroup |
PwCompareOptions.IgnoreHistory | PwCompareOptions.NullEmptyEquivStd);
if(bIgnoreLastMod) cmpOpt |= PwCompareOptions.IgnoreLastMod;
if(bIgnoreLastAccess) cmpOpt |= PwCompareOptions.IgnoreLastAccess;
foreach(PwEntry pe in m_listHistory)
{
if(pe.EqualsEntry(peData, cmpOpt, MemProtCmpMode.None)) return true;
}
return false;
}
/// <summary>
/// Delete old history items if there are too many or the history
/// size is too large.
/// <returns>If one or more history items have been deleted, <c>true</c>
/// is returned. Otherwise <c>false</c>.</returns>
/// </summary>
public bool MaintainBackups(PwDatabase pwSettings)
{
if(pwSettings == null) { Debug.Assert(false); return false; }
bool bDeleted = false;
int nMaxItems = pwSettings.HistoryMaxItems;
if(nMaxItems >= 0)
{
while(m_listHistory.UCount > (uint)nMaxItems)
{
RemoveOldestBackup();
bDeleted = true;
}
}
long lMaxSize = pwSettings.HistoryMaxSize;
if(lMaxSize >= 0)
{
while(true)
{
ulong uHistSize = 0;
foreach(PwEntry pe in m_listHistory) { uHistSize += pe.GetSize(); }
if(uHistSize > (ulong)lMaxSize)
{
RemoveOldestBackup();
bDeleted = true;
}
else break;
}
}
return bDeleted;
}
private void RemoveOldestBackup()
{
DateTime dtMin = TimeUtil.SafeMaxValueUtc;
uint idxRemove = uint.MaxValue;
for(uint u = 0; u < m_listHistory.UCount; ++u)
{
PwEntry pe = m_listHistory.GetAt(u);
if(TimeUtil.Compare(pe.LastModificationTime, dtMin, true) < 0)
{
idxRemove = u;
dtMin = pe.LastModificationTime;
}
}
if(idxRemove != uint.MaxValue) m_listHistory.RemoveAt(idxRemove);
}
public bool GetAutoTypeEnabled()
{
if(!m_listAutoType.Enabled) return false;
if(m_pParentGroup != null)
return m_pParentGroup.GetAutoTypeEnabledInherited();
return PwGroup.DefaultAutoTypeEnabled;
}
public string GetAutoTypeSequence()
{
string strSeq = m_listAutoType.DefaultSequence;
PwGroup pg = m_pParentGroup;
while(pg != null)
{
if(strSeq.Length != 0) break;
strSeq = pg.DefaultAutoTypeSequence;
pg = pg.ParentGroup;
}
if(strSeq.Length != 0) return strSeq;
if(PwDefs.IsTanEntry(this)) return PwDefs.DefaultAutoTypeSequenceTan;
return PwDefs.DefaultAutoTypeSequence;
}
public bool GetSearchingEnabled()
{
if(m_pParentGroup != null)
return m_pParentGroup.GetSearchingEnabledInherited();
return PwGroup.DefaultSearchingEnabled;
}
/// <summary>
/// Approximate the total size (in process memory) of this entry
/// in bytes (including strings, binaries and history entries).
/// </summary>
/// <returns>Size in bytes.</returns>
public ulong GetSize()
{
// This method assumes 64-bit pointers/references and Unicode
// strings (i.e. 2 bytes per character)
ulong cb = 248; // Number of bytes; approx. fixed length data
ulong cc = 0; // Number of characters
cb += (ulong)m_listStrings.UCount * 40;
foreach(KeyValuePair<string, ProtectedString> kvpStr in m_listStrings)
cc += (ulong)kvpStr.Key.Length + (ulong)kvpStr.Value.Length;
cb += (ulong)m_listBinaries.UCount * 65;
foreach(KeyValuePair<string, ProtectedBinary> kvpBin in m_listBinaries)
{
cc += (ulong)kvpBin.Key.Length;
cb += (ulong)kvpBin.Value.Length;
}
cc += (ulong)m_listAutoType.DefaultSequence.Length;
cb += (ulong)m_listAutoType.AssociationsCount * 24;
foreach(AutoTypeAssociation a in m_listAutoType.Associations)
cc += (ulong)a.WindowName.Length + (ulong)a.Sequence.Length;
cb += (ulong)m_listHistory.UCount * 8;
foreach(PwEntry peHistory in m_listHistory)
cb += peHistory.GetSize();
cc += (ulong)m_strOverrideUrl.Length;
cb += (ulong)m_vTags.Count * 8;
foreach(string strTag in m_vTags)
cc += (ulong)strTag.Length;
cb += (ulong)m_dCustomData.Count * 16;
foreach(KeyValuePair<string, string> kvp in m_dCustomData)
cc += (ulong)kvp.Key.Length + (ulong)kvp.Value.Length;
return (cb + (cc << 1));
}
public bool HasTag(string strTag)
{
if(string.IsNullOrEmpty(strTag)) { Debug.Assert(false); return false; }
for(int i = 0; i < m_vTags.Count; ++i)
{
if(m_vTags[i].Equals(strTag, StrUtil.CaseIgnoreCmp)) return true;
}
return false;
}
public bool AddTag(string strTag)
{
if(string.IsNullOrEmpty(strTag)) { Debug.Assert(false); return false; }
for(int i = 0; i < m_vTags.Count; ++i)
{
if(m_vTags[i].Equals(strTag, StrUtil.CaseIgnoreCmp)) return false;
}
m_vTags.Add(strTag);
return true;
}
public bool RemoveTag(string strTag)
{
if(string.IsNullOrEmpty(strTag)) { Debug.Assert(false); return false; }
for(int i = 0; i < m_vTags.Count; ++i)
{
if(m_vTags[i].Equals(strTag, StrUtil.CaseIgnoreCmp))
{
m_vTags.RemoveAt(i);
return true;
}
}
return false;
}
public bool IsContainedIn(PwGroup pgContainer)
{
PwGroup pgCur = m_pParentGroup;
while(pgCur != null)
{
if(pgCur == pgContainer) return true;
pgCur = pgCur.ParentGroup;
}
return false;
}
public void SetUuid(PwUuid pwNewUuid, bool bAlsoChangeHistoryUuids)
{
this.Uuid = pwNewUuid;
if(bAlsoChangeHistoryUuids)
{
foreach(PwEntry peHist in m_listHistory)
{
peHist.Uuid = pwNewUuid;
}
}
}
public void SetCreatedNow()
{
DateTime dt = DateTime.UtcNow;
m_tCreation = dt;
m_tLastAccess = dt;
}
public PwEntry Duplicate()
{
PwEntry pe = CloneDeep();
pe.SetUuid(new PwUuid(true), true);
pe.SetCreatedNow();
return pe;
}
}
public sealed class PwEntryComparer : IComparer<PwEntry>
{
private string m_strFieldName;
private bool m_bCaseInsensitive;
private bool m_bCompareNaturally;
public PwEntryComparer(string strFieldName, bool bCaseInsensitive,
bool bCompareNaturally)
{
if(strFieldName == null) throw new ArgumentNullException("strFieldName");
m_strFieldName = strFieldName;
m_bCaseInsensitive = bCaseInsensitive;
m_bCompareNaturally = bCompareNaturally;
}
public int Compare(PwEntry a, PwEntry b)
{
string strA = a.Strings.ReadSafe(m_strFieldName);
string strB = b.Strings.ReadSafe(m_strFieldName);
if(m_bCompareNaturally) return StrUtil.CompareNaturally(strA, strB);
#if ModernKeePassLib || KeePassRT
return string.Compare(strA, strB, m_bCaseInsensitive ?
StringComparison.CurrentCultureIgnoreCase : StringComparison.CurrentCulture);
#else
return string.Compare(strA, strB, m_bCaseInsensitive);
#endif
}
}
}

319
ModernKeePassLib/PwEnums.cs Normal file
View File

@@ -0,0 +1,319 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2019 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
using System;
namespace ModernKeePassLib
{
/// <summary>
/// Compression algorithm specifiers.
/// </summary>
public enum PwCompressionAlgorithm
{
/// <summary>
/// No compression.
/// </summary>
None = 0,
/// <summary>
/// GZip compression.
/// </summary>
GZip = 1,
/// <summary>
/// Virtual field: currently known number of algorithms. Should not be used
/// by plugins or libraries -- it's used internally only.
/// </summary>
Count = 2
}
/// <summary>
/// Tree traversal methods.
/// </summary>
public enum TraversalMethod
{
/// <summary>
/// Don't traverse the tree.
/// </summary>
None = 0,
/// <summary>
/// Traverse the tree in pre-order mode, i.e. first visit all items
/// in the current node, then visit all subnodes.
/// </summary>
PreOrder = 1
}
/// <summary>
/// Methods for merging password databases/entries.
/// </summary>
public enum PwMergeMethod
{
// Do not change the explicitly assigned values, otherwise
// serialization (e.g. of Ecas triggers) breaks
None = 0,
OverwriteExisting = 1,
KeepExisting = 2,
OverwriteIfNewer = 3,
CreateNewUuids = 4,
Synchronize = 5
}
/// <summary>
/// Icon identifiers for groups and password entries.
/// </summary>
public enum PwIcon
{
Key = 0,
World,
Warning,
NetworkServer,
MarkedDirectory,
UserCommunication,
Parts,
Notepad,
WorldSocket,
Identity,
PaperReady,
Digicam,
IRCommunication,
MultiKeys,
Energy,
Scanner,
WorldStar,
CDRom,
Monitor,
EMail,
Configuration,
ClipboardReady,
PaperNew,
Screen,
EnergyCareful,
EMailBox,
Disk,
Drive,
PaperQ,
TerminalEncrypted,
Console,
Printer,
ProgramIcons,
Run,
Settings,
WorldComputer,
Archive,
Homebanking,
DriveWindows,
Clock,
EMailSearch,
PaperFlag,
Memory,
TrashBin,
Note,
Expired,
Info,
Package,
Folder,
FolderOpen,
FolderPackage,
LockOpen,
PaperLocked,
Checked,
Pen,
Thumbnail,
Book,
List,
UserKey,
Tool,
Home,
Star,
Tux,
Feather,
Apple,
Wiki,
Money,
Certificate,
BlackBerry,
/// <summary>
/// Virtual identifier -- represents the number of icons.
/// </summary>
Count
}
public enum ProxyServerType
{
None = 0,
System = 1,
Manual = 2
}
public enum ProxyAuthType
{
None = 0,
/// <summary>
/// Use default user credentials (provided by the system).
/// </summary>
Default = 1,
Manual = 2,
/// <summary>
/// <c>Default</c> or <c>Manual</c>, depending on whether
/// manual credentials are available.
/// This type exists for supporting upgrading from KeePass
/// 2.28 to 2.29; the user cannot select this type.
/// </summary>
Auto = 3
}
/// <summary>
/// Comparison modes for in-memory protected objects.
/// </summary>
public enum MemProtCmpMode
{
/// <summary>
/// Ignore the in-memory protection states.
/// </summary>
None = 0,
/// <summary>
/// Ignore the in-memory protection states of standard
/// objects; do compare in-memory protection states of
/// custom objects.
/// </summary>
CustomOnly,
/// <summary>
/// Compare in-memory protection states.
/// </summary>
Full
}
[Flags]
public enum PwCompareOptions
{
None = 0x0,
/// <summary>
/// Empty standard string fields are considered to be the
/// same as non-existing standard string fields.
/// This doesn't affect custom string comparisons.
/// </summary>
NullEmptyEquivStd = 0x1,
IgnoreParentGroup = 0x2,
IgnoreLastAccess = 0x4,
IgnoreLastMod = 0x8,
IgnoreHistory = 0x10,
IgnoreLastBackup = 0x20,
// For groups:
PropertiesOnly = 0x40,
IgnoreTimes = (IgnoreLastAccess | IgnoreLastMod)
}
public enum IOAccessType
{
None = 0,
/// <summary>
/// The IO connection is being opened for reading.
/// </summary>
Read = 1,
/// <summary>
/// The IO connection is being opened for writing.
/// </summary>
Write = 2,
/// <summary>
/// The IO connection is being opened for testing
/// whether a file/object exists.
/// </summary>
Exists = 3,
/// <summary>
/// The IO connection is being opened for deleting a file/object.
/// </summary>
Delete = 4,
/// <summary>
/// The IO connection is being opened for renaming/moving a file/object.
/// </summary>
Move = 5
}
// public enum PwLogicalOp
// {
// None = 0,
// Or = 1,
// And = 2,
// NOr = 3,
// NAnd = 4
// }
[Flags]
public enum AppRunFlags
{
None = 0,
GetStdOutput = 1,
WaitForExit = 2,
// https://sourceforge.net/p/keepass/patches/84/
/// <summary>
/// This flag prevents any handles being garbage-collected
/// before the started process has terminated, without
/// blocking the current thread.
/// </summary>
GCKeepAlive = 4,
// https://sourceforge.net/p/keepass/patches/85/
DoEvents = 8,
DisableForms = 16
}
[Flags]
public enum ScaleTransformFlags
{
None = 0,
/// <summary>
/// <c>UIIcon</c> indicates that the returned image is going
/// to be displayed as icon in the UI and that it is not
/// subject to future changes in size.
/// </summary>
UIIcon = 1
}
public enum DesktopType
{
None = 0,
Windows,
Gnome,
Kde,
Unity,
Lxde,
Xfce,
Mate,
Cinnamon,
Pantheon
}
}

1750
ModernKeePassLib/PwGroup.cs Normal file

File diff suppressed because it is too large Load Diff

215
ModernKeePassLib/PwUuid.cs Normal file
View File

@@ -0,0 +1,215 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2019 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
using System;
using System.Collections.Generic;
using System.Xml;
using System.Diagnostics;
using ModernKeePassLib.Utility;
namespace ModernKeePassLib
{
// [ImmutableObject(true)]
/// <summary>
/// Represents an UUID of a password entry or group. Once created,
/// <c>PwUuid</c> objects aren't modifyable anymore (immutable).
/// </summary>
public sealed class PwUuid : IComparable<PwUuid>, IEquatable<PwUuid>
{
/// <summary>
/// Standard size in bytes of a UUID.
/// </summary>
public const uint UuidSize = 16;
/// <summary>
/// Zero UUID (all bytes are zero).
/// </summary>
public static readonly PwUuid Zero = new PwUuid(false);
private byte[] m_pbUuid = null; // Never null after constructor
/// <summary>
/// Get the 16 UUID bytes.
/// </summary>
public byte[] UuidBytes
{
get { return m_pbUuid; }
}
/// <summary>
/// Construct a new UUID object.
/// </summary>
/// <param name="bCreateNew">If this parameter is <c>true</c>, a new
/// UUID is generated. If it is <c>false</c>, the UUID is initialized
/// to zero.</param>
public PwUuid(bool bCreateNew)
{
if(bCreateNew) CreateNew();
else SetZero();
}
/// <summary>
/// Construct a new UUID object.
/// </summary>
/// <param name="uuidBytes">Initial value of the <c>PwUuid</c> object.</param>
public PwUuid(byte[] uuidBytes)
{
SetValue(uuidBytes);
}
/// <summary>
/// Create a new, random UUID.
/// </summary>
/// <returns>Returns <c>true</c> if a random UUID has been generated,
/// otherwise it returns <c>false</c>.</returns>
private void CreateNew()
{
Debug.Assert(m_pbUuid == null); // Only call from constructor
while(true)
{
m_pbUuid = Guid.NewGuid().ToByteArray();
if((m_pbUuid == null) || (m_pbUuid.Length != (int)UuidSize))
{
Debug.Assert(false);
throw new InvalidOperationException();
}
// Zero is a reserved value -- do not generate Zero
if(!Equals(PwUuid.Zero)) break;
Debug.Assert(false);
}
}
private void SetValue(byte[] uuidBytes)
{
Debug.Assert((uuidBytes != null) && (uuidBytes.Length == (int)UuidSize));
if(uuidBytes == null) throw new ArgumentNullException("uuidBytes");
if(uuidBytes.Length != (int)UuidSize) throw new ArgumentException();
Debug.Assert(m_pbUuid == null); // Only call from constructor
m_pbUuid = new byte[UuidSize];
Array.Copy(uuidBytes, m_pbUuid, (int)UuidSize);
}
private void SetZero()
{
Debug.Assert(m_pbUuid == null); // Only call from constructor
m_pbUuid = new byte[UuidSize];
// Array.Clear(m_pbUuid, 0, (int)UuidSize);
#if DEBUG
List<byte> l = new List<byte>(m_pbUuid);
Debug.Assert(l.TrueForAll(bt => (bt == 0)));
#endif
}
[Obsolete]
public bool EqualsValue(PwUuid uuid)
{
return Equals(uuid);
}
public override bool Equals(object obj)
{
return Equals(obj as PwUuid);
}
public bool Equals(PwUuid other)
{
if(other == null) { Debug.Assert(false); return false; }
for(int i = 0; i < (int)UuidSize; ++i)
{
if(m_pbUuid[i] != other.m_pbUuid[i]) return false;
}
return true;
}
private int m_h = 0;
public override int GetHashCode()
{
if(m_h == 0)
m_h = (int)MemUtil.Hash32(m_pbUuid, 0, m_pbUuid.Length);
return m_h;
}
public int CompareTo(PwUuid other)
{
if(other == null)
{
Debug.Assert(false);
throw new ArgumentNullException("other");
}
for(int i = 0; i < (int)UuidSize; ++i)
{
if(m_pbUuid[i] < other.m_pbUuid[i]) return -1;
if(m_pbUuid[i] > other.m_pbUuid[i]) return 1;
}
return 0;
}
/// <summary>
/// Convert the UUID to its string representation.
/// </summary>
/// <returns>String containing the UUID value.</returns>
public string ToHexString()
{
return MemUtil.ByteArrayToHexString(m_pbUuid);
}
#if DEBUG
public override string ToString()
{
return ToHexString();
}
#endif
}
[Obsolete]
public sealed class PwUuidComparable : IComparable<PwUuidComparable>
{
private byte[] m_pbUuid = new byte[PwUuid.UuidSize];
public PwUuidComparable(PwUuid pwUuid)
{
if(pwUuid == null) throw new ArgumentNullException("pwUuid");
Array.Copy(pwUuid.UuidBytes, m_pbUuid, (int)PwUuid.UuidSize);
}
public int CompareTo(PwUuidComparable other)
{
if(other == null) throw new ArgumentNullException("other");
for(int i = 0; i < (int)PwUuid.UuidSize; ++i)
{
if(m_pbUuid[i] < other.m_pbUuid[i]) return -1;
if(m_pbUuid[i] > other.m_pbUuid[i]) return 1;
}
return 0;
}
}
}

View File

@@ -0,0 +1,13 @@
# ModernKeePassLib
This is my adaptation of the KeePassLib (KeePass library) for the Universal Windows Platform and Windows Runtime (WinRT).
It aims at introducing as little change as possible to the original library: overall, except for namespace changes and some added classes (see below), there is almost no change.
Download the Nuget package [here](https://www.nuget.org/packages/ModernKeePassLib)
# Features
- Custom implementation of the System.Security.Cryptography.HashAlgoritm class by using WinRT equivalents
- Use of BouncyCastle PCL to implement AES key derivation features
- Lots of small changes in .NET methods (UTF8 instead of ASCII, string.)
- Disabled native functions (because not compatible with WinRT)
- Use of Splat for GfxUtil

View File

@@ -0,0 +1,546 @@
// This is a generated file!
// Do not edit manually, changes will be overwritten.
using System;
using System.Collections.Generic;
namespace ModernKeePassLib.Resources
{
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
public static class KLRes
{
private static string TryGetEx(Dictionary<string, string> dictNew,
string strName, string strDefault)
{
string strTemp;
if(dictNew.TryGetValue(strName, out strTemp))
return strTemp;
return strDefault;
}
public static void SetTranslatedStrings(Dictionary<string, string> dictNew)
{
if(dictNew == null) throw new ArgumentNullException("dictNew");
m_strCryptoStreamFailed = TryGetEx(dictNew, "CryptoStreamFailed", m_strCryptoStreamFailed);
m_strEncDataTooLarge = TryGetEx(dictNew, "EncDataTooLarge", m_strEncDataTooLarge);
m_strErrorInClipboard = TryGetEx(dictNew, "ErrorInClipboard", m_strErrorInClipboard);
m_strExpect100Continue = TryGetEx(dictNew, "Expect100Continue", m_strExpect100Continue);
m_strFatalError = TryGetEx(dictNew, "FatalError", m_strFatalError);
m_strFatalErrorText = TryGetEx(dictNew, "FatalErrorText", m_strFatalErrorText);
m_strFileCorrupted = TryGetEx(dictNew, "FileCorrupted", m_strFileCorrupted);
m_strFileHeaderCorrupted = TryGetEx(dictNew, "FileHeaderCorrupted", m_strFileHeaderCorrupted);
m_strFileIncomplete = TryGetEx(dictNew, "FileIncomplete", m_strFileIncomplete);
m_strFileIncompleteExpc = TryGetEx(dictNew, "FileIncompleteExpc", m_strFileIncompleteExpc);
m_strFileLoadFailed = TryGetEx(dictNew, "FileLoadFailed", m_strFileLoadFailed);
m_strFileLockedWrite = TryGetEx(dictNew, "FileLockedWrite", m_strFileLockedWrite);
m_strFileNewVerOrPlgReq = TryGetEx(dictNew, "FileNewVerOrPlgReq", m_strFileNewVerOrPlgReq);
m_strFileNewVerReq = TryGetEx(dictNew, "FileNewVerReq", m_strFileNewVerReq);
m_strFileSaveCorruptionWarning = TryGetEx(dictNew, "FileSaveCorruptionWarning", m_strFileSaveCorruptionWarning);
m_strFileSaveFailed = TryGetEx(dictNew, "FileSaveFailed", m_strFileSaveFailed);
m_strFileSigInvalid = TryGetEx(dictNew, "FileSigInvalid", m_strFileSigInvalid);
m_strFileUnknownCipher = TryGetEx(dictNew, "FileUnknownCipher", m_strFileUnknownCipher);
m_strFileUnknownCompression = TryGetEx(dictNew, "FileUnknownCompression", m_strFileUnknownCompression);
m_strFileVersionUnsupported = TryGetEx(dictNew, "FileVersionUnsupported", m_strFileVersionUnsupported);
m_strFinalKeyCreationFailed = TryGetEx(dictNew, "FinalKeyCreationFailed", m_strFinalKeyCreationFailed);
m_strFrameworkNotImplExcp = TryGetEx(dictNew, "FrameworkNotImplExcp", m_strFrameworkNotImplExcp);
m_strGeneral = TryGetEx(dictNew, "General", m_strGeneral);
m_strInvalidCompositeKey = TryGetEx(dictNew, "InvalidCompositeKey", m_strInvalidCompositeKey);
m_strInvalidCompositeKeyHint = TryGetEx(dictNew, "InvalidCompositeKeyHint", m_strInvalidCompositeKeyHint);
m_strInvalidDataWhileDecoding = TryGetEx(dictNew, "InvalidDataWhileDecoding", m_strInvalidDataWhileDecoding);
m_strKeePass1xHint = TryGetEx(dictNew, "KeePass1xHint", m_strKeePass1xHint);
m_strKeyBits = TryGetEx(dictNew, "KeyBits", m_strKeyBits);
m_strKeyFileDbSel = TryGetEx(dictNew, "KeyFileDbSel", m_strKeyFileDbSel);
m_strMasterSeedLengthInvalid = TryGetEx(dictNew, "MasterSeedLengthInvalid", m_strMasterSeedLengthInvalid);
m_strOldFormat = TryGetEx(dictNew, "OldFormat", m_strOldFormat);
m_strPassive = TryGetEx(dictNew, "Passive", m_strPassive);
m_strPreAuth = TryGetEx(dictNew, "PreAuth", m_strPreAuth);
m_strTimeout = TryGetEx(dictNew, "Timeout", m_strTimeout);
m_strTryAgainSecs = TryGetEx(dictNew, "TryAgainSecs", m_strTryAgainSecs);
m_strUnknownHeaderId = TryGetEx(dictNew, "UnknownHeaderId", m_strUnknownHeaderId);
m_strUnknownKdf = TryGetEx(dictNew, "UnknownKdf", m_strUnknownKdf);
m_strUserAccountKeyError = TryGetEx(dictNew, "UserAccountKeyError", m_strUserAccountKeyError);
m_strUserAgent = TryGetEx(dictNew, "UserAgent", m_strUserAgent);
}
private static readonly string[] m_vKeyNames = {
"CryptoStreamFailed",
"EncDataTooLarge",
"ErrorInClipboard",
"Expect100Continue",
"FatalError",
"FatalErrorText",
"FileCorrupted",
"FileHeaderCorrupted",
"FileIncomplete",
"FileIncompleteExpc",
"FileLoadFailed",
"FileLockedWrite",
"FileNewVerOrPlgReq",
"FileNewVerReq",
"FileSaveCorruptionWarning",
"FileSaveFailed",
"FileSigInvalid",
"FileUnknownCipher",
"FileUnknownCompression",
"FileVersionUnsupported",
"FinalKeyCreationFailed",
"FrameworkNotImplExcp",
"General",
"InvalidCompositeKey",
"InvalidCompositeKeyHint",
"InvalidDataWhileDecoding",
"KeePass1xHint",
"KeyBits",
"KeyFileDbSel",
"MasterSeedLengthInvalid",
"OldFormat",
"Passive",
"PreAuth",
"Timeout",
"TryAgainSecs",
"UnknownHeaderId",
"UnknownKdf",
"UserAccountKeyError",
"UserAgent"
};
public static string[] GetKeyNames()
{
return m_vKeyNames;
}
private static string m_strCryptoStreamFailed =
@"Failed to initialize encryption/decryption stream!";
/// <summary>
/// Look up a localized string similar to
/// 'Failed to initialize encryption/decryption stream!'.
/// </summary>
public static string CryptoStreamFailed
{
get { return m_strCryptoStreamFailed; }
}
private static string m_strEncDataTooLarge =
@"The data is too large to be encrypted/decrypted securely using {PARAM}.";
/// <summary>
/// Look up a localized string similar to
/// 'The data is too large to be encrypted/decrypted securely using {PARAM}.'.
/// </summary>
public static string EncDataTooLarge
{
get { return m_strEncDataTooLarge; }
}
private static string m_strErrorInClipboard =
@"An extended error report has been copied to the clipboard.";
/// <summary>
/// Look up a localized string similar to
/// 'An extended error report has been copied to the clipboard.'.
/// </summary>
public static string ErrorInClipboard
{
get { return m_strErrorInClipboard; }
}
private static string m_strExpect100Continue =
@"Expect 100-Continue responses";
/// <summary>
/// Look up a localized string similar to
/// 'Expect 100-Continue responses'.
/// </summary>
public static string Expect100Continue
{
get { return m_strExpect100Continue; }
}
private static string m_strFatalError =
@"Fatal Error";
/// <summary>
/// Look up a localized string similar to
/// 'Fatal Error'.
/// </summary>
public static string FatalError
{
get { return m_strFatalError; }
}
private static string m_strFatalErrorText =
@"A fatal error has occurred!";
/// <summary>
/// Look up a localized string similar to
/// 'A fatal error has occurred!'.
/// </summary>
public static string FatalErrorText
{
get { return m_strFatalErrorText; }
}
private static string m_strFileCorrupted =
@"The file is corrupted.";
/// <summary>
/// Look up a localized string similar to
/// 'The file is corrupted.'.
/// </summary>
public static string FileCorrupted
{
get { return m_strFileCorrupted; }
}
private static string m_strFileHeaderCorrupted =
@"The file header is corrupted.";
/// <summary>
/// Look up a localized string similar to
/// 'The file header is corrupted.'.
/// </summary>
public static string FileHeaderCorrupted
{
get { return m_strFileHeaderCorrupted; }
}
private static string m_strFileIncomplete =
@"Data is missing at the end of the file, i.e. the file is incomplete.";
/// <summary>
/// Look up a localized string similar to
/// 'Data is missing at the end of the file, i.e. the file is incomplete.'.
/// </summary>
public static string FileIncomplete
{
get { return m_strFileIncomplete; }
}
private static string m_strFileIncompleteExpc =
@"Less data than expected could be read from the file.";
/// <summary>
/// Look up a localized string similar to
/// 'Less data than expected could be read from the file.'.
/// </summary>
public static string FileIncompleteExpc
{
get { return m_strFileIncompleteExpc; }
}
private static string m_strFileLoadFailed =
@"Failed to load the specified file!";
/// <summary>
/// Look up a localized string similar to
/// 'Failed to load the specified file!'.
/// </summary>
public static string FileLoadFailed
{
get { return m_strFileLoadFailed; }
}
private static string m_strFileLockedWrite =
@"The file is locked, because the following user is currently writing to it:";
/// <summary>
/// Look up a localized string similar to
/// 'The file is locked, because the following user is currently writing to it:'.
/// </summary>
public static string FileLockedWrite
{
get { return m_strFileLockedWrite; }
}
private static string m_strFileNewVerOrPlgReq =
@"A newer KeePass version or a plugin is required to open this file.";
/// <summary>
/// Look up a localized string similar to
/// 'A newer KeePass version or a plugin is required to open this file.'.
/// </summary>
public static string FileNewVerOrPlgReq
{
get { return m_strFileNewVerOrPlgReq; }
}
private static string m_strFileNewVerReq =
@"A newer KeePass version is required to open this file.";
/// <summary>
/// Look up a localized string similar to
/// 'A newer KeePass version is required to open this file.'.
/// </summary>
public static string FileNewVerReq
{
get { return m_strFileNewVerReq; }
}
private static string m_strFileSaveCorruptionWarning =
@"The target file might be corrupted. Please try saving again. If that fails, save the database to a different location.";
/// <summary>
/// Look up a localized string similar to
/// 'The target file might be corrupted. Please try saving again. If that fails, save the database to a different location.'.
/// </summary>
public static string FileSaveCorruptionWarning
{
get { return m_strFileSaveCorruptionWarning; }
}
private static string m_strFileSaveFailed =
@"Failed to save the current database to the specified location!";
/// <summary>
/// Look up a localized string similar to
/// 'Failed to save the current database to the specified location!'.
/// </summary>
public static string FileSaveFailed
{
get { return m_strFileSaveFailed; }
}
private static string m_strFileSigInvalid =
@"The file signature is invalid. Either the file isn't a KeePass database file at all or it is corrupted.";
/// <summary>
/// Look up a localized string similar to
/// 'The file signature is invalid. Either the file isn&#39;t a KeePass database file at all or it is corrupted.'.
/// </summary>
public static string FileSigInvalid
{
get { return m_strFileSigInvalid; }
}
private static string m_strFileUnknownCipher =
@"The file is encrypted using an unknown encryption algorithm!";
/// <summary>
/// Look up a localized string similar to
/// 'The file is encrypted using an unknown encryption algorithm!'.
/// </summary>
public static string FileUnknownCipher
{
get { return m_strFileUnknownCipher; }
}
private static string m_strFileUnknownCompression =
@"The file is compressed using an unknown compression algorithm!";
/// <summary>
/// Look up a localized string similar to
/// 'The file is compressed using an unknown compression algorithm!'.
/// </summary>
public static string FileUnknownCompression
{
get { return m_strFileUnknownCompression; }
}
private static string m_strFileVersionUnsupported =
@"The file version is unsupported.";
/// <summary>
/// Look up a localized string similar to
/// 'The file version is unsupported.'.
/// </summary>
public static string FileVersionUnsupported
{
get { return m_strFileVersionUnsupported; }
}
private static string m_strFinalKeyCreationFailed =
@"Failed to create the final encryption/decryption key!";
/// <summary>
/// Look up a localized string similar to
/// 'Failed to create the final encryption/decryption key!'.
/// </summary>
public static string FinalKeyCreationFailed
{
get { return m_strFinalKeyCreationFailed; }
}
private static string m_strFrameworkNotImplExcp =
@"The .NET framework/runtime under which KeePass is currently running does not support this operation.";
/// <summary>
/// Look up a localized string similar to
/// 'The .NET framework/runtime under which KeePass is currently running does not support this operation.'.
/// </summary>
public static string FrameworkNotImplExcp
{
get { return m_strFrameworkNotImplExcp; }
}
private static string m_strGeneral =
@"General";
/// <summary>
/// Look up a localized string similar to
/// 'General'.
/// </summary>
public static string General
{
get { return m_strGeneral; }
}
private static string m_strInvalidCompositeKey =
@"The composite key is invalid!";
/// <summary>
/// Look up a localized string similar to
/// 'The composite key is invalid!'.
/// </summary>
public static string InvalidCompositeKey
{
get { return m_strInvalidCompositeKey; }
}
private static string m_strInvalidCompositeKeyHint =
@"Make sure the composite key is correct and try again.";
/// <summary>
/// Look up a localized string similar to
/// 'Make sure the composite key is correct and try again.'.
/// </summary>
public static string InvalidCompositeKeyHint
{
get { return m_strInvalidCompositeKeyHint; }
}
private static string m_strInvalidDataWhileDecoding =
@"Found invalid data while decoding.";
/// <summary>
/// Look up a localized string similar to
/// 'Found invalid data while decoding.'.
/// </summary>
public static string InvalidDataWhileDecoding
{
get { return m_strInvalidDataWhileDecoding; }
}
private static string m_strKeePass1xHint =
@"In order to import KeePass 1.x KDB files, create a new 2.x database file and click 'File' -> 'Import' in the main menu. In the import dialog, choose 'KeePass KDB (1.x)' as file format.";
/// <summary>
/// Look up a localized string similar to
/// 'In order to import KeePass 1.x KDB files, create a new 2.x database file and click &#39;File&#39; -&gt; &#39;Import&#39; in the main menu. In the import dialog, choose &#39;KeePass KDB (1.x)&#39; as file format.'.
/// </summary>
public static string KeePass1xHint
{
get { return m_strKeePass1xHint; }
}
private static string m_strKeyBits =
@"{PARAM}-bit key";
/// <summary>
/// Look up a localized string similar to
/// '{PARAM}-bit key'.
/// </summary>
public static string KeyBits
{
get { return m_strKeyBits; }
}
private static string m_strKeyFileDbSel =
@"Database files cannot be used as key files.";
/// <summary>
/// Look up a localized string similar to
/// 'Database files cannot be used as key files.'.
/// </summary>
public static string KeyFileDbSel
{
get { return m_strKeyFileDbSel; }
}
private static string m_strMasterSeedLengthInvalid =
@"The length of the master key seed is invalid!";
/// <summary>
/// Look up a localized string similar to
/// 'The length of the master key seed is invalid!'.
/// </summary>
public static string MasterSeedLengthInvalid
{
get { return m_strMasterSeedLengthInvalid; }
}
private static string m_strOldFormat =
@"The selected file appears to be an old format";
/// <summary>
/// Look up a localized string similar to
/// 'The selected file appears to be an old format'.
/// </summary>
public static string OldFormat
{
get { return m_strOldFormat; }
}
private static string m_strPassive =
@"Passive";
/// <summary>
/// Look up a localized string similar to
/// 'Passive'.
/// </summary>
public static string Passive
{
get { return m_strPassive; }
}
private static string m_strPreAuth =
@"Pre-authenticate";
/// <summary>
/// Look up a localized string similar to
/// 'Pre-authenticate'.
/// </summary>
public static string PreAuth
{
get { return m_strPreAuth; }
}
private static string m_strTimeout =
@"Timeout";
/// <summary>
/// Look up a localized string similar to
/// 'Timeout'.
/// </summary>
public static string Timeout
{
get { return m_strTimeout; }
}
private static string m_strTryAgainSecs =
@"Please try it again in a few seconds.";
/// <summary>
/// Look up a localized string similar to
/// 'Please try it again in a few seconds.'.
/// </summary>
public static string TryAgainSecs
{
get { return m_strTryAgainSecs; }
}
private static string m_strUnknownHeaderId =
@"Unknown header ID!";
/// <summary>
/// Look up a localized string similar to
/// 'Unknown header ID!'.
/// </summary>
public static string UnknownHeaderId
{
get { return m_strUnknownHeaderId; }
}
private static string m_strUnknownKdf =
@"Unknown key derivation function!";
/// <summary>
/// Look up a localized string similar to
/// 'Unknown key derivation function!'.
/// </summary>
public static string UnknownKdf
{
get { return m_strUnknownKdf; }
}
private static string m_strUserAccountKeyError =
@"The operating system did not grant KeePass read/write access to the user profile folder, where the protected user key is stored.";
/// <summary>
/// Look up a localized string similar to
/// 'The operating system did not grant KeePass read/write access to the user profile folder, where the protected user key is stored.'.
/// </summary>
public static string UserAccountKeyError
{
get { return m_strUserAccountKeyError; }
}
private static string m_strUserAgent =
@"User agent";
/// <summary>
/// Look up a localized string similar to
/// 'User agent'.
/// </summary>
public static string UserAgent
{
get { return m_strUserAgent; }
}
}
}

View File

@@ -0,0 +1,52 @@
// This is a generated file!
// Do not edit manually, changes will be overwritten.
using System;
using System.Collections.Generic;
namespace ModernKeePassLib.Resources
{
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
public static class KSRes
{
private static string TryGetEx(Dictionary<string, string> dictNew,
string strName, string strDefault)
{
string strTemp;
if(dictNew.TryGetValue(strName, out strTemp))
return strTemp;
return strDefault;
}
public static void SetTranslatedStrings(Dictionary<string, string> dictNew)
{
if(dictNew == null) throw new ArgumentNullException("dictNew");
m_strTest = TryGetEx(dictNew, "Test", m_strTest);
}
private static readonly string[] m_vKeyNames = {
"Test"
};
public static string[] GetKeyNames()
{
return m_vKeyNames;
}
private static string m_strTest =
@"Test";
/// <summary>
/// Look up a localized string similar to
/// 'Test'.
/// </summary>
public static string Test
{
get { return m_strTest; }
}
}
}

View File

@@ -0,0 +1,465 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2019 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
using System;
using System.Diagnostics;
using System.Threading;
#if !KeePassUAP
using System.Security.Cryptography;
#endif
using ModernKeePassLib.Cryptography;
using ModernKeePassLib.Cryptography.Cipher;
using ModernKeePassLib.Native;
using ModernKeePassLib.Utility;
#if KeePassLibSD
using KeePassLibSD;
#endif
namespace ModernKeePassLib.Security
{
[Flags]
public enum PbCryptFlags
{
None = 0,
Encrypt = 1,
Decrypt = 2
}
public delegate void PbCryptDelegate(byte[] pbData, PbCryptFlags cf,
long lID);
/// <summary>
/// A protected binary, i.e. a byte array that is encrypted in memory.
/// A <c>ProtectedBinary</c> object is immutable and thread-safe.
/// </summary>
public sealed class ProtectedBinary : IEquatable<ProtectedBinary>
{
private const int BlockSize = 16;
private static PbCryptDelegate g_fExtCrypt = null;
/// <summary>
/// A plugin can provide a custom memory protection method
/// by assigning a non-null delegate to this property.
/// </summary>
public static PbCryptDelegate ExtCrypt
{
get { return g_fExtCrypt; }
set { g_fExtCrypt = value; }
}
// Local copy of the delegate that was used for encryption,
// in order to allow correct decryption even when the global
// delegate changes
private PbCryptDelegate m_fExtCrypt = null;
private enum PbMemProt
{
None = 0,
ProtectedMemory, // DPAPI on Windows
ChaCha20,
ExtCrypt
}
// ProtectedMemory is supported only on Windows 2000 SP3 and higher
#if !KeePassLibSD
private static bool? g_obProtectedMemorySupported = null;
#endif
private static bool ProtectedMemorySupported
{
get
{
#if KeePassLibSD
return false;
#else
bool? ob = g_obProtectedMemorySupported;
if(ob.HasValue) return ob.Value;
// Mono does not implement any encryption for ProtectedMemory
// on Linux (Mono uses DPAPI on Windows);
// https://sourceforge.net/p/keepass/feature-requests/1907/
if(NativeLib.IsUnix())
{
g_obProtectedMemorySupported = false;
return false;
}
ob = false;
try // Test whether ProtectedMemory is supported
{
// BlockSize * 3 in order to test encryption for multiple
// blocks, but not introduce a power of 2 as factor
byte[] pb = new byte[ProtectedBinary.BlockSize * 3];
for(int i = 0; i < pb.Length; ++i) pb[i] = (byte)i;
ProtectedMemory.Protect(pb, MemoryProtectionScope.SameProcess);
for(int i = 0; i < pb.Length; ++i)
{
if(pb[i] != (byte)i) { ob = true; break; }
}
}
catch(Exception) { } // Windows 98 / ME
g_obProtectedMemorySupported = ob;
return ob.Value;
#endif
}
}
private static long g_lCurID = 0;
private long m_lID;
private byte[] m_pbData; // Never null
// The real length of the data; this value can be different from
// m_pbData.Length, as the length of m_pbData always is a multiple
// of BlockSize (required for ProtectedMemory)
private uint m_uDataLen;
private bool m_bProtected; // Protection requested by the caller
private PbMemProt m_mp = PbMemProt.None; // Actual protection
private readonly object m_objSync = new object();
private static byte[] g_pbKey32 = null;
/// <summary>
/// A flag specifying whether the <c>ProtectedBinary</c> object has
/// turned on memory protection or not.
/// </summary>
public bool IsProtected
{
get { return m_bProtected; }
}
/// <summary>
/// Length of the stored data.
/// </summary>
public uint Length
{
get { return m_uDataLen; }
}
/// <summary>
/// Construct a new, empty protected binary data object.
/// Protection is disabled.
/// </summary>
public ProtectedBinary()
{
Init(false, MemUtil.EmptyByteArray, 0, 0);
}
/// <summary>
/// Construct a new protected binary data object.
/// </summary>
/// <param name="bEnableProtection">If this paremeter is <c>true</c>,
/// the data will be encrypted in memory. If it is <c>false</c>, the
/// data is stored in plain-text in the process memory.</param>
/// <param name="pbData">Value of the protected object.
/// The input parameter is not modified and
/// <c>ProtectedBinary</c> doesn't take ownership of the data,
/// i.e. the caller is responsible for clearing it.</param>
public ProtectedBinary(bool bEnableProtection, byte[] pbData)
{
if(pbData == null) throw new ArgumentNullException("pbData"); // For .Length
Init(bEnableProtection, pbData, 0, pbData.Length);
}
/// <summary>
/// Construct a new protected binary data object.
/// </summary>
/// <param name="bEnableProtection">If this paremeter is <c>true</c>,
/// the data will be encrypted in memory. If it is <c>false</c>, the
/// data is stored in plain-text in the process memory.</param>
/// <param name="pbData">Value of the protected object.
/// The input parameter is not modified and
/// <c>ProtectedBinary</c> doesn't take ownership of the data,
/// i.e. the caller is responsible for clearing it.</param>
/// <param name="iOffset">Offset for <paramref name="pbData" />.</param>
/// <param name="cbSize">Size for <paramref name="pbData" />.</param>
public ProtectedBinary(bool bEnableProtection, byte[] pbData,
int iOffset, int cbSize)
{
Init(bEnableProtection, pbData, iOffset, cbSize);
}
/// <summary>
/// Construct a new protected binary data object.
/// Copy the data from a <c>XorredBuffer</c> object.
/// </summary>
/// <param name="bEnableProtection">Enable protection or not.</param>
/// <param name="xb"><c>XorredBuffer</c> object containing the data.</param>
public ProtectedBinary(bool bEnableProtection, XorredBuffer xb)
{
if(xb == null) { Debug.Assert(false); throw new ArgumentNullException("xb"); }
byte[] pb = xb.ReadPlainText();
try { Init(bEnableProtection, pb, 0, pb.Length); }
finally { if(bEnableProtection) MemUtil.ZeroByteArray(pb); }
}
private void Init(bool bEnableProtection, byte[] pbData, int iOffset,
int cbSize)
{
if(pbData == null) throw new ArgumentNullException("pbData");
if(iOffset < 0) throw new ArgumentOutOfRangeException("iOffset");
if(cbSize < 0) throw new ArgumentOutOfRangeException("cbSize");
if(iOffset > (pbData.Length - cbSize))
throw new ArgumentOutOfRangeException("cbSize");
#if KeePassLibSD
m_lID = ++g_lCurID;
#else
m_lID = Interlocked.Increment(ref g_lCurID);
#endif
m_bProtected = bEnableProtection;
m_uDataLen = (uint)cbSize;
const int bs = ProtectedBinary.BlockSize;
int nBlocks = cbSize / bs;
if((nBlocks * bs) < cbSize) ++nBlocks;
Debug.Assert((nBlocks * bs) >= cbSize);
m_pbData = new byte[nBlocks * bs];
Array.Copy(pbData, iOffset, m_pbData, 0, cbSize);
Encrypt();
}
private void Encrypt()
{
Debug.Assert(m_mp == PbMemProt.None);
// Nothing to do if caller didn't request protection
if(!m_bProtected) return;
// ProtectedMemory.Protect throws for data size == 0
if(m_pbData.Length == 0) return;
PbCryptDelegate f = g_fExtCrypt;
if(f != null)
{
f(m_pbData, PbCryptFlags.Encrypt, m_lID);
m_fExtCrypt = f;
m_mp = PbMemProt.ExtCrypt;
return;
}
if(ProtectedBinary.ProtectedMemorySupported)
{
ProtectedMemory.Protect(m_pbData, MemoryProtectionScope.SameProcess);
m_mp = PbMemProt.ProtectedMemory;
return;
}
byte[] pbKey32 = g_pbKey32;
if(pbKey32 == null)
{
pbKey32 = GetRandom32();
byte[] pbUpd = Interlocked.Exchange<byte[]>(ref g_pbKey32, pbKey32);
if(pbUpd != null) pbKey32 = pbUpd;
}
byte[] pbIV = new byte[12];
MemUtil.UInt64ToBytesEx((ulong)m_lID, pbIV, 4);
using(ChaCha20Cipher c = new ChaCha20Cipher(pbKey32, pbIV, true))
{
c.Encrypt(m_pbData, 0, m_pbData.Length);
}
m_mp = PbMemProt.ChaCha20;
}
private void Decrypt()
{
if(m_pbData.Length == 0) return;
if(m_mp == PbMemProt.ProtectedMemory)
ProtectedMemory.Unprotect(m_pbData, MemoryProtectionScope.SameProcess);
else if(m_mp == PbMemProt.ChaCha20)
{
byte[] pbIV = new byte[12];
MemUtil.UInt64ToBytesEx((ulong)m_lID, pbIV, 4);
using(ChaCha20Cipher c = new ChaCha20Cipher(g_pbKey32, pbIV, true))
{
c.Decrypt(m_pbData, 0, m_pbData.Length);
}
}
else if(m_mp == PbMemProt.ExtCrypt)
m_fExtCrypt(m_pbData, PbCryptFlags.Decrypt, m_lID);
else { Debug.Assert(m_mp == PbMemProt.None); }
m_mp = PbMemProt.None;
}
/// <summary>
/// Get a copy of the protected data as a byte array.
/// Please note that the returned byte array is not protected and
/// can therefore been read by any other application.
/// Make sure that your clear it properly after usage.
/// </summary>
/// <returns>Unprotected byte array. This is always a copy of the internal
/// protected data and can therefore be cleared safely.</returns>
public byte[] ReadData()
{
if(m_uDataLen == 0) return MemUtil.EmptyByteArray;
byte[] pbReturn = new byte[m_uDataLen];
lock(m_objSync)
{
Decrypt();
Array.Copy(m_pbData, pbReturn, (int)m_uDataLen);
Encrypt();
}
return pbReturn;
}
/// <summary>
/// Get the data xorred with bytes from a <c>CryptoRandomStream</c>.
/// </summary>
public byte[] ReadXorredData(CryptoRandomStream crsRandomSource)
{
if(crsRandomSource == null) { Debug.Assert(false); throw new ArgumentNullException("crsRandomSource"); }
byte[] pbData = ReadData();
int cb = pbData.Length;
byte[] pbPad = crsRandomSource.GetRandomBytes((uint)cb);
Debug.Assert(pbPad.Length == cb);
for(int i = 0; i < cb; ++i)
pbData[i] ^= pbPad[i];
MemUtil.ZeroByteArray(pbPad);
return pbData;
}
private int? m_hash = null;
public override int GetHashCode()
{
if(m_hash.HasValue) return m_hash.Value;
int h = (m_bProtected ? 0x7B11D289 : 0);
byte[] pb = ReadData();
unchecked
{
for(int i = 0; i < pb.Length; ++i)
h = (h << 3) + h + (int)pb[i];
}
if(m_bProtected) MemUtil.ZeroByteArray(pb);
m_hash = h;
return h;
}
public override bool Equals(object obj)
{
return this.Equals(obj as ProtectedBinary, true);
}
public bool Equals(ProtectedBinary other)
{
return this.Equals(other, true);
}
public bool Equals(ProtectedBinary other, bool bCheckProtEqual)
{
if(other == null) return false; // No assert
if(object.ReferenceEquals(this, other)) return true; // Perf. opt.
if(bCheckProtEqual && (m_bProtected != other.m_bProtected))
return false;
if(m_uDataLen != other.m_uDataLen) return false;
byte[] pbL = ReadData(), pbR = null;
bool bEq;
try
{
pbR = other.ReadData();
bEq = MemUtil.ArraysEqual(pbL, pbR);
}
finally
{
if(m_bProtected) MemUtil.ZeroByteArray(pbL);
if(other.m_bProtected && (pbR != null)) MemUtil.ZeroByteArray(pbR);
}
return bEq;
}
private static byte[] GetRandom32()
{
// Do not use CryptoRandom here, as it uses ProtectedBinary;
// we would have an infinite recursion when trying to
// construct a ProtectedBinary object
// return CryptoRandom.Instance.GetRandomBytes(32);
byte[] pbAll = new byte[32 + 16 + 8 + 4];
int i = 0;
try
{
RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
byte[] pb = new byte[32];
rng.GetBytes(pb);
Array.Copy(pb, 0, pbAll, i, 32);
i += 32;
MemUtil.ZeroByteArray(pb);
// In .NET 2.0, RNGCryptoServiceProvider does not
// implement IDisposable; in later .NET versions it does
MemUtil.DisposeIfPossible(rng);
}
catch(Exception) { Debug.Assert(false); }
try // In case RNGCryptoServiceProvider doesn't work properly
{
byte[] pb = Guid.NewGuid().ToByteArray();
Array.Copy(pb, 0, pbAll, i, 16);
i += 16;
MemUtil.Int64ToBytesEx(DateTime.UtcNow.ToBinary(), pbAll, i);
i += 8;
MemUtil.Int32ToBytesEx(Environment.TickCount, pbAll, i);
i += 4;
}
catch(Exception) { Debug.Assert(false); }
Debug.Assert(i == pbAll.Length);
byte[] pbHash = CryptoUtil.HashSha256(pbAll);
MemUtil.ZeroByteArray(pbAll);
return pbHash;
}
}
}

View File

@@ -0,0 +1,434 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2019 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
using System;
using System.Diagnostics;
using System.Text;
using ModernKeePassLib.Cryptography;
using ModernKeePassLib.Utility;
#if KeePassLibSD
using KeePassLibSD;
#endif
// SecureString objects are limited to 65536 characters, don't use
namespace ModernKeePassLib.Security
{
/// <summary>
/// A string that is protected in process memory.
/// <c>ProtectedString</c> objects are immutable and thread-safe.
/// </summary>
#if (DEBUG && !KeePassLibSD)
[DebuggerDisplay("{ReadString()}")]
#endif
public sealed class ProtectedString
{
// Exactly one of the following will be non-null
private ProtectedBinary m_pbUtf8 = null;
private string m_strPlainText = null;
private bool m_bIsProtected;
private static readonly ProtectedString m_psEmpty = new ProtectedString();
/// <summary>
/// Get an empty <c>ProtectedString</c> object, without protection.
/// </summary>
public static ProtectedString Empty
{
get { return m_psEmpty; }
}
private static readonly ProtectedString m_psEmptyEx = new ProtectedString(
true, new byte[0]);
/// <summary>
/// Get an empty <c>ProtectedString</c> object, with protection turned on.
/// </summary>
public static ProtectedString EmptyEx
{
get { return m_psEmptyEx; }
}
/// <summary>
/// A flag specifying whether the <c>ProtectedString</c> object
/// has turned on memory protection or not.
/// </summary>
public bool IsProtected
{
get { return m_bIsProtected; }
}
public bool IsEmpty
{
get
{
ProtectedBinary p = m_pbUtf8; // Local ref for thread-safety
if(p != null) return (p.Length == 0);
Debug.Assert(m_strPlainText != null);
return (m_strPlainText.Length == 0);
}
}
private int m_nCachedLength = -1;
/// <summary>
/// Length of the protected string, in characters.
/// </summary>
public int Length
{
get
{
if(m_nCachedLength >= 0) return m_nCachedLength;
ProtectedBinary p = m_pbUtf8; // Local ref for thread-safety
if(p != null)
{
byte[] pbPlain = p.ReadData();
try { m_nCachedLength = StrUtil.Utf8.GetCharCount(pbPlain); }
finally { MemUtil.ZeroByteArray(pbPlain); }
}
else
{
Debug.Assert(m_strPlainText != null);
m_nCachedLength = m_strPlainText.Length;
}
return m_nCachedLength;
}
}
/// <summary>
/// Construct a new protected string object. Protection is
/// disabled.
/// </summary>
public ProtectedString()
{
Init(false, string.Empty);
}
/// <summary>
/// Construct a new protected string. The string is initialized
/// to the value supplied in the parameters.
/// </summary>
/// <param name="bEnableProtection">If this parameter is <c>true</c>,
/// the string will be protected in memory (encrypted). If it
/// is <c>false</c>, the string will be stored as plain-text.</param>
/// <param name="strValue">The initial string value.</param>
public ProtectedString(bool bEnableProtection, string strValue)
{
Init(bEnableProtection, strValue);
}
/// <summary>
/// Construct a new protected string. The string is initialized
/// to the value supplied in the parameters (UTF-8 encoded string).
/// </summary>
/// <param name="bEnableProtection">If this parameter is <c>true</c>,
/// the string will be protected in memory (encrypted). If it
/// is <c>false</c>, the string will be stored as plain-text.</param>
/// <param name="vUtf8Value">The initial string value, encoded as
/// UTF-8 byte array. This parameter won't be modified; the caller
/// is responsible for clearing it.</param>
public ProtectedString(bool bEnableProtection, byte[] vUtf8Value)
{
Init(bEnableProtection, vUtf8Value);
}
/// <summary>
/// Construct a new protected string. The string is initialized
/// to the value passed in the <c>XorredBuffer</c> object.
/// </summary>
/// <param name="bEnableProtection">Enable protection or not.</param>
/// <param name="xb"><c>XorredBuffer</c> object containing the
/// string in UTF-8 representation. The UTF-8 string must not
/// be <c>null</c>-terminated.</param>
public ProtectedString(bool bEnableProtection, XorredBuffer xb)
{
if(xb == null) { Debug.Assert(false); throw new ArgumentNullException("xb"); }
byte[] pb = xb.ReadPlainText();
try { Init(bEnableProtection, pb); }
finally { if(bEnableProtection) MemUtil.ZeroByteArray(pb); }
}
private void Init(bool bEnableProtection, string str)
{
if(str == null) throw new ArgumentNullException("str");
m_bIsProtected = bEnableProtection;
// As the string already is in memory and immutable,
// protection would be useless
m_strPlainText = str;
}
private void Init(bool bEnableProtection, byte[] pbUtf8)
{
if(pbUtf8 == null) throw new ArgumentNullException("pbUtf8");
m_bIsProtected = bEnableProtection;
if(bEnableProtection)
m_pbUtf8 = new ProtectedBinary(true, pbUtf8);
else
m_strPlainText = StrUtil.Utf8.GetString(pbUtf8, 0, pbUtf8.Length);
}
/// <summary>
/// Convert the protected string to a standard string object.
/// Be careful with this function, as the returned string object
/// isn't protected anymore and stored in plain-text in the
/// process memory.
/// </summary>
/// <returns>Plain-text string. Is never <c>null</c>.</returns>
public string ReadString()
{
if(m_strPlainText != null) return m_strPlainText;
byte[] pb = ReadUtf8();
string str = ((pb.Length == 0) ? string.Empty :
StrUtil.Utf8.GetString(pb, 0, pb.Length));
// No need to clear pb
// As the text is now visible in process memory anyway,
// there's no need to protect it anymore (strings are
// immutable and thus cannot be overwritten)
m_strPlainText = str;
m_pbUtf8 = null; // Thread-safe order
return str;
}
/// <summary>
/// Read out the string and return it as a char array.
/// The returned array is not protected and should be cleared by
/// the caller.
/// </summary>
/// <returns>Plain-text char array.</returns>
public char[] ReadChars()
{
if(m_strPlainText != null) return m_strPlainText.ToCharArray();
byte[] pb = ReadUtf8();
char[] v;
try { v = StrUtil.Utf8.GetChars(pb); }
finally { MemUtil.ZeroByteArray(pb); }
return v;
}
/// <summary>
/// Read out the string and return a byte array that contains the
/// string encoded using UTF-8.
/// The returned array is not protected and should be cleared by
/// the caller.
/// </summary>
/// <returns>Plain-text UTF-8 byte array.</returns>
public byte[] ReadUtf8()
{
ProtectedBinary p = m_pbUtf8; // Local ref for thread-safety
if(p != null) return p.ReadData();
return StrUtil.Utf8.GetBytes(m_strPlainText);
}
/// <summary>
/// Get the string as an UTF-8 sequence xorred with bytes
/// from a <c>CryptoRandomStream</c>.
/// </summary>
public byte[] ReadXorredString(CryptoRandomStream crsRandomSource)
{
if(crsRandomSource == null) { Debug.Assert(false); throw new ArgumentNullException("crsRandomSource"); }
byte[] pbData = ReadUtf8();
int cb = pbData.Length;
byte[] pbPad = crsRandomSource.GetRandomBytes((uint)cb);
Debug.Assert(pbPad.Length == cb);
for(int i = 0; i < cb; ++i)
pbData[i] ^= pbPad[i];
MemUtil.ZeroByteArray(pbPad);
return pbData;
}
public ProtectedString WithProtection(bool bProtect)
{
if(bProtect == m_bIsProtected) return this;
byte[] pb = ReadUtf8();
// No need to clear pb; either the current or the new object is unprotected
return new ProtectedString(bProtect, pb);
}
public bool Equals(ProtectedString ps, bool bCheckProtEqual)
{
if(ps == null) throw new ArgumentNullException("ps");
if(object.ReferenceEquals(this, ps)) return true; // Perf. opt.
bool bPA = m_bIsProtected, bPB = ps.m_bIsProtected;
if(bCheckProtEqual && (bPA != bPB)) return false;
if(!bPA && !bPB) return (ReadString() == ps.ReadString());
byte[] pbA = ReadUtf8(), pbB = null;
bool bEq;
try
{
pbB = ps.ReadUtf8();
bEq = MemUtil.ArraysEqual(pbA, pbB);
}
finally
{
if(bPA) MemUtil.ZeroByteArray(pbA);
if(bPB && (pbB != null)) MemUtil.ZeroByteArray(pbB);
}
return bEq;
}
public ProtectedString Insert(int iStart, string strInsert)
{
if(iStart < 0) throw new ArgumentOutOfRangeException("iStart");
if(strInsert == null) throw new ArgumentNullException("strInsert");
if(strInsert.Length == 0) return this;
if(!m_bIsProtected)
return new ProtectedString(false, ReadString().Insert(
iStart, strInsert));
UTF8Encoding utf8 = StrUtil.Utf8;
char[] v = ReadChars(), vNew = null;
byte[] pbNew = null;
ProtectedString ps;
try
{
if(iStart > v.Length)
throw new ArgumentOutOfRangeException("iStart");
char[] vIns = strInsert.ToCharArray();
vNew = new char[v.Length + vIns.Length];
Array.Copy(v, 0, vNew, 0, iStart);
Array.Copy(vIns, 0, vNew, iStart, vIns.Length);
Array.Copy(v, iStart, vNew, iStart + vIns.Length,
v.Length - iStart);
pbNew = utf8.GetBytes(vNew);
ps = new ProtectedString(true, pbNew);
Debug.Assert(utf8.GetString(pbNew, 0, pbNew.Length) ==
ReadString().Insert(iStart, strInsert));
}
finally
{
MemUtil.ZeroArray<char>(v);
if(vNew != null) MemUtil.ZeroArray<char>(vNew);
if(pbNew != null) MemUtil.ZeroByteArray(pbNew);
}
return ps;
}
public ProtectedString Remove(int iStart, int nCount)
{
if(iStart < 0) throw new ArgumentOutOfRangeException("iStart");
if(nCount < 0) throw new ArgumentOutOfRangeException("nCount");
if(nCount == 0) return this;
if(!m_bIsProtected)
return new ProtectedString(false, ReadString().Remove(
iStart, nCount));
UTF8Encoding utf8 = StrUtil.Utf8;
char[] v = ReadChars(), vNew = null;
byte[] pbNew = null;
ProtectedString ps;
try
{
if((iStart + nCount) > v.Length)
throw new ArgumentException("(iStart + nCount) > v.Length");
vNew = new char[v.Length - nCount];
Array.Copy(v, 0, vNew, 0, iStart);
Array.Copy(v, iStart + nCount, vNew, iStart, v.Length -
(iStart + nCount));
pbNew = utf8.GetBytes(vNew);
ps = new ProtectedString(true, pbNew);
Debug.Assert(utf8.GetString(pbNew, 0, pbNew.Length) ==
ReadString().Remove(iStart, nCount));
}
finally
{
MemUtil.ZeroArray<char>(v);
if(vNew != null) MemUtil.ZeroArray<char>(vNew);
if(pbNew != null) MemUtil.ZeroByteArray(pbNew);
}
return ps;
}
public static ProtectedString operator +(ProtectedString a, ProtectedString b)
{
if(a == null) throw new ArgumentNullException("a");
if(b == null) throw new ArgumentNullException("b");
if(b.IsEmpty) return a;
if(a.IsEmpty) return b;
if(!a.IsProtected && !b.IsProtected)
return new ProtectedString(false, a.ReadString() + b.ReadString());
char[] vA = a.ReadChars(), vB = null, vNew = null;
byte[] pbNew = null;
ProtectedString ps;
try
{
vB = b.ReadChars();
vNew = new char[vA.Length + vB.Length];
Array.Copy(vA, vNew, vA.Length);
Array.Copy(vB, 0, vNew, vA.Length, vB.Length);
pbNew = StrUtil.Utf8.GetBytes(vNew);
ps = new ProtectedString(true, pbNew);
}
finally
{
MemUtil.ZeroArray<char>(vA);
if(vB != null) MemUtil.ZeroArray<char>(vB);
if(vNew != null) MemUtil.ZeroArray<char>(vNew);
if(pbNew != null) MemUtil.ZeroByteArray(pbNew);
}
return ps;
}
public static ProtectedString operator +(ProtectedString a, string b)
{
ProtectedString psB = new ProtectedString(false, b);
return (a + psB);
}
}
}

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