diff --git a/src/MPTest/MPTest.csproj b/src/MPTest/MPTest.csproj
new file mode 100644
index 00000000..a81e31e8
--- /dev/null
+++ b/src/MPTest/MPTest.csproj
@@ -0,0 +1,72 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {96A3EA5A-7024-479F-A5B1-06654D0867A3}
+ Exe
+ Properties
+ MPTest
+ MPTest
+ v4.5
+ 512
+
+
+
+ AnyCPU
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+ false
+
+
+ AnyCPU
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+ false
+
+
+
+ False
+ ..\..\..\..\..\..\..\Program Files (x86)\Microsoft Visual Studio 11.0\Common7\IDE\PublicAssemblies\Microsoft.VisualStudio.QualityTools.UnitTestFramework.dll
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {2f7cb5b4-ac2a-4790-b0f3-42e6c9a060d5}
+ MasterPassword
+
+
+
+
+
\ No newline at end of file
diff --git a/src/MPTest/Main.cs b/src/MPTest/Main.cs
new file mode 100644
index 00000000..e7681290
--- /dev/null
+++ b/src/MPTest/Main.cs
@@ -0,0 +1,16 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace MPTest
+{
+ class Program
+ {
+ static int Main()
+ {
+ return 0;
+ }
+ }
+}
diff --git a/src/MPTest/MpTest.cs b/src/MPTest/MpTest.cs
new file mode 100644
index 00000000..3dfb0d15
--- /dev/null
+++ b/src/MPTest/MpTest.cs
@@ -0,0 +1,69 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Security.Cryptography;
+using System.Text;
+using MasterPassword;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+namespace MPTest
+{
+
+ [TestClass()]
+ public class MpAlgorithmTests
+ {
+ private static byte[] HashHMAC(byte[] key, byte[] message)
+ {
+ var hash = new HMACSHA256(key);
+ return hash.ComputeHash(message);
+ }
+
+ [TestMethod()]
+ public void GenerateKeyTest()
+ {
+ sbyte[] expectedRes =
+ {
+ -103, 59, -64, -27, 39, -62, 10, -76, -24, -28, -111, -75, 13, -128, -80, -101, 39, 41, -98, -22,
+ -42, 61, -75, 38, -107, -40, 111, 61, 108, 63, 60, 82, 92, -39, 72, 14, 14, -26, 93, 67, 83, 25, -32, 5, 32, 102,
+ -126, 24, 15, 65, 9, 17, 0, 123, 91, 105, -46, -99, -64, 123, -12, 80, -37, -77
+ };
+ var result = MpAlgorithm.GetKeyForPassword("u", "test");
+
+ Assert.IsTrue(expectedRes.SequenceEqual(result));
+
+ }
+ [TestMethod]
+ public void GenerateContentTest()
+ {
+ var key = MpAlgorithm.GetKeyForPassword("u", "test");
+ string password = MpAlgorithm.GenerateContent("Long Password", "strn", key, 1, HashHMAC);
+ Assert.AreEqual("LapdKebv2_Tele", password);
+ }
+
+ [TestMethod]
+ public void GenerateContentWithUmlautsTest()
+ {
+ var key = MpAlgorithm.GetKeyForPassword("uÜ", "testÖ");
+ string password = MpAlgorithm.GenerateContent("Long Password", "strnÄ", key, 1, HashHMAC);
+ Assert.AreEqual("YepiHedo1*Kada", password);
+ }
+
+ [TestMethod]
+ public void GenerateContentWithUmlautsAndCounterTest()
+ {
+ var key = MpAlgorithm.GetKeyForPassword("uÜ", "testÖ");
+ string password = MpAlgorithm.GenerateContent("Long Password", "strnÄ", key, 42, HashHMAC);
+ Assert.AreEqual("Gasc3!YeluMoqb", password);
+ }
+
+ [TestMethod]
+ public void GetKeyWithUmlautsTest()
+ {
+ var key = MpAlgorithm.GetKeyForPassword("uÜ", "testÖ");
+ sbyte[] expected =
+ {
+ -53, -69, -89, 48, 122, 56, 34, 13, -70, -103, 102, 90, -96, -75, 45, 68, 43, 67, 97, 60, 84, -90, 98, -95, -2, -2, 99, -60, -121, -2, -26, -45, 53, -31, 47, 0, -46, -97, 77, -41, 63, -15, -30, 60, 4, -120, 32, 122, -94, 42, 122, -103, -61, -115, 75, -123, -15, 47, 61, -100, -119, 115, 118, 82
+ };
+ Assert.IsTrue(expected.SequenceEqual(key));
+ }
+ }
+}
diff --git a/src/MPTest/Properties/AssemblyInfo.cs b/src/MPTest/Properties/AssemblyInfo.cs
new file mode 100644
index 00000000..62ad4dd0
--- /dev/null
+++ b/src/MPTest/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// Allgemeine Informationen über eine Assembly werden über die folgenden
+// Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern,
+// die mit einer Assembly verknüpft sind.
+[assembly: AssemblyTitle("MPTest")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("MPTest")]
+[assembly: AssemblyCopyright("Copyright © 2014")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Durch Festlegen von ComVisible auf "false" werden die Typen in dieser Assembly unsichtbar
+// für COM-Komponenten. Wenn Sie auf einen Typ in dieser Assembly von
+// COM zugreifen müssen, legen Sie das ComVisible-Attribut für diesen Typ auf "true" fest.
+[assembly: ComVisible(false)]
+
+// Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird
+[assembly: Guid("5fbae881-f9e4-4aa1-adff-4721cad61250")]
+
+// Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten:
+//
+// Hauptversion
+// Nebenversion
+// Buildnummer
+// Revision
+//
+// Sie können alle Werte angeben oder die standardmäßigen Build- und Revisionsnummern
+// übernehmen, indem Sie "*" eingeben:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/src/MPTest/app.config b/src/MPTest/app.config
new file mode 100644
index 00000000..c5e1daef
--- /dev/null
+++ b/src/MPTest/app.config
@@ -0,0 +1,3 @@
+
+
+
diff --git a/src/MasterPassword/MasterPassword.csproj b/src/MasterPassword/MasterPassword.csproj
new file mode 100644
index 00000000..5047d18e
--- /dev/null
+++ b/src/MasterPassword/MasterPassword.csproj
@@ -0,0 +1,57 @@
+
+
+
+
+ 11.0
+ Debug
+ AnyCPU
+ {2F7CB5B4-AC2A-4790-B0F3-42E6C9A060D5}
+ Library
+ Properties
+ MasterPassword
+ MasterPassword
+ v4.5
+ Profile7
+ 512
+ {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+
+
+
+
+
+
+ ..\packages\CryptSharpOfficial.2.0.0.0\lib\CryptSharp.dll
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/MasterPassword/MpAlgorithm.cs b/src/MasterPassword/MpAlgorithm.cs
new file mode 100644
index 00000000..0a8fdd2f
--- /dev/null
+++ b/src/MasterPassword/MpAlgorithm.cs
@@ -0,0 +1,120 @@
+using System;
+using System.Linq;
+using System.Text;
+using CryptSharp.Utility;
+using MasterPassword.Data;
+
+
+namespace MasterPassword
+{
+ public partial class MpAlgorithm
+ {
+
+ static readonly PList PlistData = new PList(plist);
+ private const int MP_N = 32768;
+ private const int MP_r = 8;
+ private const int MP_p = 2;
+ private const int MP_dkLen = 64;
+
+ static sbyte[] ToSignedByteArray(byte[] unsigned)
+ {
+ sbyte[] signed = new sbyte[unsigned.Length];
+ Buffer.BlockCopy(unsigned, 0, signed, 0, unsigned.Length);
+ return signed;
+ }
+ static byte[] ToUnsignedByteArray(sbyte[] signed)
+ {
+ byte[] unsigned = new byte[signed.Length];
+ Buffer.BlockCopy(signed, 0, unsigned, 0, signed.Length);
+ return unsigned;
+ }
+
+ public static byte[] GetKeyForPassword(string user, string password)
+ {
+ var salt = Combine(Encoding.UTF8.GetBytes("com.lyndir.masterpassword"),
+ IntAsByteArray(user.Length),
+ Encoding.UTF8.GetBytes(user));
+ var ssalt = ToSignedByteArray(salt);
+ var spwd = ToSignedByteArray(Encoding.UTF8.GetBytes(password));
+ var key = SCrypt.ComputeDerivedKey(Encoding.UTF8.GetBytes(password),
+ salt,
+ MP_N,
+ MP_r,
+ MP_p,null, MP_dkLen);
+
+ sbyte[] signed = ToSignedByteArray(key);
+ return signed;
+ }
+
+
+ static byte[] Combine(params byte[][] arrays)
+ {
+ byte[] ret = new byte[arrays.Sum(x => x.Length)];
+ int offset = 0;
+ foreach (byte[] data in arrays)
+ {
+ Buffer.BlockCopy(data, 0, ret, offset, data.Length);
+ offset += data.Length;
+ }
+ return ret;
+ }
+ static byte[] IntAsByteArray(int intValue)
+ {
+ byte[] intBytes = BitConverter.GetBytes(intValue);
+ if (BitConverter.IsLittleEndian)
+ Array.Reverse(intBytes);
+ return intBytes;
+ }
+
+ public static String GenerateContent(string elementType, string siteName, sbyte[] key, int counter, Func hmacFunc)
+ {
+ if (counter == 0)
+ {
+ throw new Exception("counter==0 not support in .net. ");
+ //TODO: what does this line do in Java?
+ //counter = (int) (System.currentTimeMillis() / (300 * 1000)) * 300;
+ }
+
+
+ byte[] nameLengthBytes = IntAsByteArray(siteName.Length);
+ byte[] counterBytes =IntAsByteArray(counter);
+
+ sbyte[] seed = ToSignedByteArray(hmacFunc( ToUnsignedByteArray(key), Combine( Encoding.UTF8.GetBytes("com.lyndir.masterpassword"), //
+ nameLengthBytes, //
+ Encoding.UTF8.GetBytes(siteName), //
+ counterBytes ) ) );
+ //logger.trc( "seed is: %s", CryptUtils.encodeBase64( seed ) );
+
+ //Preconditions.checkState( seed.length > 0 );
+ int templateIndex = seed[0] & 0xFF; // Mask the integer's sign.
+ //MPTemplate template = templates.getTemplateForTypeAtRollingIndex( type, templateIndex );
+ //MPTemplate template = null;
+
+
+
+ var templatesLongPwd = PlistData["MPElementGeneratedEntity"]["Long Password"];
+ string template = templatesLongPwd[templateIndex%templatesLongPwd.Count];
+ //logger.trc( "type: %s, template: %s", type, template );
+
+ StringBuilder password = new StringBuilder( template.Length );
+ for (int i = 0; i < template.Length; ++i) {
+ int characterIndex = seed[i + 1] & 0xFF; // Mask the integer's sign.
+
+ char c = template[i];
+
+ PList listOfCharacterSets = PlistData["MPCharacterClasses"];
+ var listOfCharacters = listOfCharacterSets.Single(kvp => { return kvp.Key == c.ToString(); }).Value;
+
+ char passwordCharacter = listOfCharacters[characterIndex % listOfCharacters.Length];
+ /*logger.trc( "class: %s, index: %d, byte: 0x%02X, chosen password character: %s", characterClass, characterIndex, seed[i + 1],
+ passwordCharacter );
+ */
+ password.Append(passwordCharacter);
+ }
+
+ return password.ToString();
+ }
+
+
+ }
+}
diff --git a/src/MasterPassword/MpPasswordPlistData.cs b/src/MasterPassword/MpPasswordPlistData.cs
new file mode 100644
index 00000000..e537f52b
--- /dev/null
+++ b/src/MasterPassword/MpPasswordPlistData.cs
@@ -0,0 +1,90 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace MasterPassword
+{
+ public partial class MpAlgorithm
+ {
+ private const string plist = @"
+
+
+ MPElementGeneratedEntity
+
+ Maximum Security Password
+
+ anoxxxxxxxxxxxxxxxxx
+ axxxxxxxxxxxxxxxxxno
+
+ Long Password
+
+ CvcvnoCvcvCvcv
+ CvcvCvcvnoCvcv
+ CvcvCvcvCvcvno
+ CvccnoCvcvCvcv
+ CvccCvcvnoCvcv
+ CvccCvcvCvcvno
+ CvcvnoCvccCvcv
+ CvcvCvccnoCvcv
+ CvcvCvccCvcvno
+ CvcvnoCvcvCvcc
+ CvcvCvcvnoCvcc
+ CvcvCvcvCvccno
+ CvccnoCvccCvcv
+ CvccCvccnoCvcv
+ CvccCvccCvcvno
+ CvcvnoCvccCvcc
+ CvcvCvccnoCvcc
+ CvcvCvccCvccno
+ CvccnoCvcvCvcc
+ CvccCvcvnoCvcc
+ CvccCvcvCvccno
+
+ Medium Password
+
+ CvcnoCvc
+ CvcCvcno
+
+ Basic Password
+
+ aaanaaan
+ aannaaan
+ aaannaaa
+
+ Short Password
+
+ Cvcn
+
+ PIN
+
+ nnnn
+
+
+ MPCharacterClasses
+
+ V
+ AEIOU
+ C
+ BCDFGHJKLMNPQRSTVWXYZ
+ v
+ aeiou
+ c
+ bcdfghjklmnpqrstvwxyz
+ A
+ AEIOUBCDFGHJKLMNPQRSTVWXYZ
+ a
+ AEIOUaeiouBCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz
+ n
+ 0123456789
+ o
+ @&%?,=[]_:-+*$#!'^~;()/.
+ x
+ AEIOUaeiouBCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz0123456789!@#$%^&*()
+
+
+
+";
+ }
+}
diff --git a/src/MasterPassword/PList.cs b/src/MasterPassword/PList.cs
new file mode 100644
index 00000000..ac7a789c
--- /dev/null
+++ b/src/MasterPassword/PList.cs
@@ -0,0 +1,83 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Xml;
+using System.Xml.Linq;
+
+namespace MasterPassword.Data
+{
+ public class PList : Dictionary
+ {
+ public PList()
+ {
+ }
+
+ public PList(string data)
+ {
+ Read(data);
+ }
+
+ public void Read(string data)
+ {
+ Clear();
+
+ //XDocument doc = XDocument.Load(file);
+ XDocument doc = XDocument.Load(XmlReader.Create(new StringReader(data)));
+ XElement plist = doc.Element("plist");
+ XElement dict = plist.Element("dict");
+
+ var dictElements = dict.Elements();
+ Parse(this, dictElements);
+ }
+
+ private void Parse(PList dict, IEnumerable elements)
+ {
+ for (int i = 0; i < elements.Count(); i += 2)
+ {
+ XElement key = elements.ElementAt(i);
+ XElement val = elements.ElementAt(i + 1);
+
+ dict[key.Value] = ParseValue(val);
+ }
+ }
+
+ private List ParseArray(IEnumerable elements)
+ {
+ List list = new List();
+ foreach (XElement e in elements)
+ {
+ dynamic one = ParseValue(e);
+ list.Add(one);
+ }
+
+ return list;
+ }
+
+ private dynamic ParseValue(XElement val)
+ {
+ switch (val.Name.ToString())
+ {
+ case "string":
+ return val.Value;
+ case "integer":
+ return int.Parse(val.Value);
+ case "real":
+ return float.Parse(val.Value);
+ case "true":
+ return true;
+ case "false":
+ return false;
+ case "dict":
+ PList plist = new PList();
+ Parse(plist, val.Elements());
+ return plist;
+ case "array":
+ List list = ParseArray(val.Elements());
+ return list;
+ default:
+ throw new ArgumentException("Unsupported");
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/MasterPassword/Properties/AssemblyInfo.cs b/src/MasterPassword/Properties/AssemblyInfo.cs
new file mode 100644
index 00000000..30195286
--- /dev/null
+++ b/src/MasterPassword/Properties/AssemblyInfo.cs
@@ -0,0 +1,30 @@
+using System.Resources;
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// Allgemeine Informationen über eine Assembly werden über folgende
+// Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern,
+// die einer Assembly zugeordnet sind.
+[assembly: AssemblyTitle("MasterPassword")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("MasterPassword")]
+[assembly: AssemblyCopyright("Copyright © 2014")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+[assembly: NeutralResourcesLanguage("de")]
+
+// Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten:
+//
+// Hauptversion
+// Nebenversion
+// Buildnummer
+// Revision
+//
+// Sie können alle Werte angeben oder die standardmäßigen Build- und Revisionsnummern
+// durch Einsatz von '*', wie in nachfolgendem Beispiel:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/src/MasterPassword/packages.config b/src/MasterPassword/packages.config
new file mode 100644
index 00000000..b386490c
--- /dev/null
+++ b/src/MasterPassword/packages.config
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file