2
0

7 Commits 50667925da ... 5a0ceac861

Autor SHA1 Nachricht Datum
  maxep 5a0ceac861 Update README vor 6 Jahren
  maxep 0a4bd42c6d Add timestamp protocol vor 6 Jahren
  maxep 3af2eb831b Remove default tests vor 6 Jahren
  maxep 6311e974ad Add Crypto tests vor 6 Jahren
  maxep 6f50e117a2 Modify `Entry` protocol vor 6 Jahren
  maxep 78ff539e2e Add XML support. vor 6 Jahren
  maxep ad1156f91b Rename `fields` to `properties`. vor 6 Jahren

+ 687 - 0
Fixtures/password-five-entries-ladder.xml

@@ -0,0 +1,687 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<KeePassFile>
+    <Meta>
+        <Generator>MacPass</Generator>
+        <HeaderHash>oQlsDi8Tv2zpDOzpzUPnNBmJoItOhikqO0aDxB+3YyM=</HeaderHash>
+        <DatabaseName>Database</DatabaseName>
+        <DatabaseNameChanged>2018-10-16T10:17:31Z</DatabaseNameChanged>
+        <DatabaseDescription />
+        <DatabaseDescriptionChanged>2018-10-16T10:17:31Z</DatabaseDescriptionChanged>
+        <DefaultUserName />
+        <DefaultUserNameChanged>2018-10-16T10:17:31Z</DefaultUserNameChanged>
+        <MaintenanceHistoryDays>365</MaintenanceHistoryDays>
+        <Color />
+        <MasterKeyChanged>2018-10-16T10:17:46Z</MasterKeyChanged>
+        <MasterKeyChangeRec>-1</MasterKeyChangeRec>
+        <MasterKeyChangeForce>-1</MasterKeyChangeForce>
+        <MemoryProtection>
+            <ProtectTitle>False</ProtectTitle>
+            <ProtectUserName>False</ProtectUserName>
+            <ProtectPassword>True</ProtectPassword>
+            <ProtectURL>False</ProtectURL>
+            <ProtectNotes>False</ProtectNotes>
+        </MemoryProtection>
+        <RecycleBinEnabled>False</RecycleBinEnabled>
+        <RecycleBinUUID>AAAAAAAAAAAAAAAAAAAAAA==</RecycleBinUUID>
+        <RecycleBinChanged>2018-10-16T10:17:31Z</RecycleBinChanged>
+        <EntryTemplatesGroup />
+        <EntryTemplatesGroupChanged>2018-10-16T10:17:31Z</EntryTemplatesGroupChanged>
+        <HistoryMaxItems>10</HistoryMaxItems>
+        <HistoryMaxSize>6291456</HistoryMaxSize>
+        <LastSelectedGroup>BNhuTL6mSL2j6F98eXlPKw==</LastSelectedGroup>
+        <LastTopVisibleGroup>AAAAAAAAAAAAAAAAAAAAAA==</LastTopVisibleGroup>
+        <Binaries />
+        <CustomData />
+    </Meta>
+    <Root>
+        <Group>
+            <UUID>gl3mtgC2S66D6lzMfIh60Q==</UUID>
+            <Name>General</Name>
+            <Notes />
+            <IconID>48</IconID>
+            <Times>
+                <LastModificationTime>2018-10-16T10:17:31Z</LastModificationTime>
+                <CreationTime>2018-10-16T10:17:31Z</CreationTime>
+                <LastAccessTime>2018-10-16T10:17:31Z</LastAccessTime>
+                <ExpiryTime>4001-01-01T00:00:00Z</ExpiryTime>
+                <Expires>False</Expires>
+                <UsageCount>0</UsageCount>
+                <LocationChanged>2018-10-16T10:17:31Z</LocationChanged>
+            </Times>
+            <IsExpanded>True</IsExpanded>
+            <DefaultAutoTypeSequence />
+            <EnableAutoType>null</EnableAutoType>
+            <EnableSearching>null</EnableSearching>
+            <LastTopVisibleEntry>AAAAAAAAAAAAAAAAAAAAAA==</LastTopVisibleEntry>
+            <Group>
+                <UUID>pT7yNh2KRXuYq/PWQmJs+g==</UUID>
+                <Name>Windows</Name>
+                <Notes />
+                <IconID>38</IconID>
+                <Times>
+                    <LastModificationTime>2018-10-16T10:17:31Z</LastModificationTime>
+                    <CreationTime>2018-10-16T10:17:31Z</CreationTime>
+                    <LastAccessTime>2018-10-16T10:17:31Z</LastAccessTime>
+                    <ExpiryTime>4001-01-01T00:00:00Z</ExpiryTime>
+                    <Expires>False</Expires>
+                    <UsageCount>0</UsageCount>
+                    <LocationChanged>2018-10-17T09:52:49Z</LocationChanged>
+                </Times>
+                <IsExpanded>False</IsExpanded>
+                <DefaultAutoTypeSequence />
+                <EnableAutoType>null</EnableAutoType>
+                <EnableSearching>null</EnableSearching>
+                <LastTopVisibleEntry>AAAAAAAAAAAAAAAAAAAAAA==</LastTopVisibleEntry>
+            </Group>
+            <Group>
+                <UUID>UprnvsYNSmSDft7g7ZLi5A==</UUID>
+                <Name>Network</Name>
+                <Notes />
+                <IconID>3</IconID>
+                <Times>
+                    <LastModificationTime>2018-10-16T10:17:31Z</LastModificationTime>
+                    <CreationTime>2018-10-16T10:17:31Z</CreationTime>
+                    <LastAccessTime>2018-10-16T10:17:31Z</LastAccessTime>
+                    <ExpiryTime>4001-01-01T00:00:00Z</ExpiryTime>
+                    <Expires>False</Expires>
+                    <UsageCount>0</UsageCount>
+                    <LocationChanged>2018-10-17T09:52:49Z</LocationChanged>
+                </Times>
+                <IsExpanded>False</IsExpanded>
+                <DefaultAutoTypeSequence />
+                <EnableAutoType>null</EnableAutoType>
+                <EnableSearching>null</EnableSearching>
+                <LastTopVisibleEntry>AAAAAAAAAAAAAAAAAAAAAA==</LastTopVisibleEntry>
+            </Group>
+            <Group>
+                <UUID>O2yCuxckRxetGWs312O+aw==</UUID>
+                <Name>Internet</Name>
+                <Notes />
+                <IconID>1</IconID>
+                <Times>
+                    <LastModificationTime>2018-10-16T10:17:31Z</LastModificationTime>
+                    <CreationTime>2018-10-16T10:17:31Z</CreationTime>
+                    <LastAccessTime>2018-10-16T10:17:31Z</LastAccessTime>
+                    <ExpiryTime>4001-01-01T00:00:00Z</ExpiryTime>
+                    <Expires>False</Expires>
+                    <UsageCount>0</UsageCount>
+                    <LocationChanged>2018-10-17T09:52:49Z</LocationChanged>
+                </Times>
+                <IsExpanded>False</IsExpanded>
+                <DefaultAutoTypeSequence />
+                <EnableAutoType>null</EnableAutoType>
+                <EnableSearching>null</EnableSearching>
+                <LastTopVisibleEntry>AAAAAAAAAAAAAAAAAAAAAA==</LastTopVisibleEntry>
+            </Group>
+            <Group>
+                <UUID>gTzQoHdaTgOQY2NiL8944g==</UUID>
+                <Name>EMail</Name>
+                <Notes />
+                <IconID>19</IconID>
+                <Times>
+                    <LastModificationTime>2018-10-16T10:17:31Z</LastModificationTime>
+                    <CreationTime>2018-10-16T10:17:31Z</CreationTime>
+                    <LastAccessTime>2018-10-16T10:17:31Z</LastAccessTime>
+                    <ExpiryTime>4001-01-01T00:00:00Z</ExpiryTime>
+                    <Expires>False</Expires>
+                    <UsageCount>0</UsageCount>
+                    <LocationChanged>2018-10-17T09:52:49Z</LocationChanged>
+                </Times>
+                <IsExpanded>False</IsExpanded>
+                <DefaultAutoTypeSequence />
+                <EnableAutoType>null</EnableAutoType>
+                <EnableSearching>null</EnableSearching>
+                <LastTopVisibleEntry>AAAAAAAAAAAAAAAAAAAAAA==</LastTopVisibleEntry>
+            </Group>
+            <Group>
+                <UUID>tia6bQr3SZajKJBRqwI7/g==</UUID>
+                <Name>Homebanking</Name>
+                <Notes />
+                <IconID>37</IconID>
+                <Times>
+                    <LastModificationTime>2018-10-16T10:17:31Z</LastModificationTime>
+                    <CreationTime>2018-10-16T10:17:31Z</CreationTime>
+                    <LastAccessTime>2018-10-16T10:17:31Z</LastAccessTime>
+                    <ExpiryTime>4001-01-01T00:00:00Z</ExpiryTime>
+                    <Expires>False</Expires>
+                    <UsageCount>0</UsageCount>
+                    <LocationChanged>2018-10-17T09:52:49Z</LocationChanged>
+                </Times>
+                <IsExpanded>False</IsExpanded>
+                <DefaultAutoTypeSequence />
+                <EnableAutoType>null</EnableAutoType>
+                <EnableSearching>null</EnableSearching>
+                <LastTopVisibleEntry>AAAAAAAAAAAAAAAAAAAAAA==</LastTopVisibleEntry>
+            </Group>
+            <Group>
+                <UUID>BNhuTL6mSL2j6F98eXlPKw==</UUID>
+                <Name>New Group</Name>
+                <Notes />
+                <IconID>48</IconID>
+                <Times>
+                    <LastModificationTime>2018-10-16T10:17:37Z</LastModificationTime>
+                    <CreationTime>2018-10-16T10:17:37Z</CreationTime>
+                    <LastAccessTime>2018-10-16T10:17:37Z</LastAccessTime>
+                    <ExpiryTime>4001-01-01T00:00:00Z</ExpiryTime>
+                    <Expires>False</Expires>
+                    <UsageCount>0</UsageCount>
+                    <LocationChanged>2018-10-17T09:52:49Z</LocationChanged>
+                </Times>
+                <IsExpanded>False</IsExpanded>
+                <DefaultAutoTypeSequence />
+                <EnableAutoType>null</EnableAutoType>
+                <EnableSearching>null</EnableSearching>
+                <LastTopVisibleEntry>AAAAAAAAAAAAAAAAAAAAAA==</LastTopVisibleEntry>
+                <Entry>
+                    <UUID>IDsxLCDlT+iNk398rApJNA==</UUID>
+                    <IconID>0</IconID>
+                    <ForegroundColor />
+                    <BackgroundColor />
+                    <OverrideURL />
+                    <Tags />
+                    <Times>
+                        <LastModificationTime>2018-10-17T12:10:15Z</LastModificationTime>
+                        <CreationTime>2018-10-16T10:18:01Z</CreationTime>
+                        <LastAccessTime>2018-10-16T10:18:01Z</LastAccessTime>
+                        <ExpiryTime>4001-01-01T00:00:00Z</ExpiryTime>
+                        <Expires>False</Expires>
+                        <UsageCount>0</UsageCount>
+                        <LocationChanged>2018-10-17T09:52:49Z</LocationChanged>
+                    </Times>
+                    <String>
+                        <Key>Title</Key>
+                        <Value>Entry 1</Value>
+                    </String>
+                    <String>
+                        <Key>UserName</Key>
+                        <Value />
+                    </String>
+                    <String>
+                        <Key>Password</Key>
+                        <Value Protected="True">AOwr9oAa</Value>
+                    </String>
+                    <String>
+                        <Key>URL</Key>
+                        <Value />
+                    </String>
+                    <String>
+                        <Key>Notes</Key>
+                        <Value />
+                    </String>
+                    <AutoType>
+                        <Enabled>True</Enabled>
+                        <DataTransferObfuscation>0</DataTransferObfuscation>
+                    </AutoType>
+                    <History>
+                        <Entry>
+                            <UUID>IDsxLCDlT+iNk398rApJNA==</UUID>
+                            <IconID>0</IconID>
+                            <ForegroundColor />
+                            <BackgroundColor />
+                            <OverrideURL />
+                            <Tags />
+                            <Times>
+                                <LastModificationTime>2018-10-16T10:18:01Z</LastModificationTime>
+                                <CreationTime>2018-10-16T10:18:01Z</CreationTime>
+                                <LastAccessTime>2018-10-16T10:18:01Z</LastAccessTime>
+                                <ExpiryTime>4001-01-01T00:00:00Z</ExpiryTime>
+                                <Expires>False</Expires>
+                                <UsageCount>0</UsageCount>
+                                <LocationChanged>2018-10-16T10:18:01Z</LocationChanged>
+                            </Times>
+                            <String>
+                                <Key>Title</Key>
+                                <Value>New Entry</Value>
+                            </String>
+                            <String>
+                                <Key>UserName</Key>
+                                <Value />
+                            </String>
+                            <String>
+                                <Key>Password</Key>
+                                <Value Protected="True">tYumIe8XorQLkvrE</Value>
+                            </String>
+                            <String>
+                                <Key>URL</Key>
+                                <Value />
+                            </String>
+                            <String>
+                                <Key>Notes</Key>
+                                <Value />
+                            </String>
+                            <AutoType>
+                                <Enabled>True</Enabled>
+                                <DataTransferObfuscation>0</DataTransferObfuscation>
+                            </AutoType>
+                        </Entry>
+                        <Entry>
+                            <UUID>IDsxLCDlT+iNk398rApJNA==</UUID>
+                            <IconID>0</IconID>
+                            <ForegroundColor />
+                            <BackgroundColor />
+                            <OverrideURL />
+                            <Tags />
+                            <Times>
+                                <LastModificationTime>2018-10-16T10:18:05Z</LastModificationTime>
+                                <CreationTime>2018-10-16T10:18:01Z</CreationTime>
+                                <LastAccessTime>2018-10-16T10:18:01Z</LastAccessTime>
+                                <ExpiryTime>4001-01-01T00:00:00Z</ExpiryTime>
+                                <Expires>False</Expires>
+                                <UsageCount>0</UsageCount>
+                                <LocationChanged>2018-10-17T09:52:49Z</LocationChanged>
+                            </Times>
+                            <String>
+                                <Key>Title</Key>
+                                <Value>Entry 1</Value>
+                            </String>
+                            <String>
+                                <Key>UserName</Key>
+                                <Value />
+                            </String>
+                            <String>
+                                <Key>Password</Key>
+                                <Value Protected="True">1yeO0TxwNduEZ3pT</Value>
+                            </String>
+                            <String>
+                                <Key>URL</Key>
+                                <Value />
+                            </String>
+                            <String>
+                                <Key>Notes</Key>
+                                <Value />
+                            </String>
+                            <AutoType>
+                                <Enabled>True</Enabled>
+                                <DataTransferObfuscation>0</DataTransferObfuscation>
+                            </AutoType>
+                        </Entry>
+                    </History>
+                </Entry>
+                <Entry>
+                    <UUID>RL4QMkD2SrGboJVNst4woQ==</UUID>
+                    <IconID>0</IconID>
+                    <ForegroundColor />
+                    <BackgroundColor />
+                    <OverrideURL />
+                    <Tags />
+                    <Times>
+                        <LastModificationTime>2018-10-17T14:25:11Z</LastModificationTime>
+                        <CreationTime>2018-10-17T14:24:51Z</CreationTime>
+                        <LastAccessTime>2018-10-17T14:24:51Z</LastAccessTime>
+                        <ExpiryTime>4001-01-01T00:00:00Z</ExpiryTime>
+                        <Expires>False</Expires>
+                        <UsageCount>0</UsageCount>
+                        <LocationChanged>2018-10-17T14:24:51Z</LocationChanged>
+                    </Times>
+                    <String>
+                        <Key>Title</Key>
+                        <Value>Entry 2</Value>
+                    </String>
+                    <String>
+                        <Key>UserName</Key>
+                        <Value />
+                    </String>
+                    <String>
+                        <Key>Password</Key>
+                        <Value Protected="True">lsq+w7si</Value>
+                    </String>
+                    <String>
+                        <Key>URL</Key>
+                        <Value />
+                    </String>
+                    <String>
+                        <Key>Notes</Key>
+                        <Value />
+                    </String>
+                    <AutoType>
+                        <Enabled>True</Enabled>
+                        <DataTransferObfuscation>0</DataTransferObfuscation>
+                    </AutoType>
+                    <History>
+                        <Entry>
+                            <UUID>RL4QMkD2SrGboJVNst4woQ==</UUID>
+                            <IconID>0</IconID>
+                            <ForegroundColor />
+                            <BackgroundColor />
+                            <OverrideURL />
+                            <Tags />
+                            <Times>
+                                <LastModificationTime>2018-10-17T14:24:51Z</LastModificationTime>
+                                <CreationTime>2018-10-17T14:24:51Z</CreationTime>
+                                <LastAccessTime>2018-10-17T14:24:51Z</LastAccessTime>
+                                <ExpiryTime>4001-01-01T00:00:00Z</ExpiryTime>
+                                <Expires>False</Expires>
+                                <UsageCount>0</UsageCount>
+                                <LocationChanged>2018-10-17T14:24:51Z</LocationChanged>
+                            </Times>
+                            <String>
+                                <Key>Title</Key>
+                                <Value>New Entry</Value>
+                            </String>
+                            <String>
+                                <Key>UserName</Key>
+                                <Value />
+                            </String>
+                            <String>
+                                <Key>Password</Key>
+                                <Value Protected="True">yc+x2Cdhnw9Rgwnq</Value>
+                            </String>
+                            <String>
+                                <Key>URL</Key>
+                                <Value />
+                            </String>
+                            <String>
+                                <Key>Notes</Key>
+                                <Value />
+                            </String>
+                            <AutoType>
+                                <Enabled>True</Enabled>
+                                <DataTransferObfuscation>0</DataTransferObfuscation>
+                            </AutoType>
+                        </Entry>
+                        <Entry>
+                            <UUID>RL4QMkD2SrGboJVNst4woQ==</UUID>
+                            <IconID>0</IconID>
+                            <ForegroundColor />
+                            <BackgroundColor />
+                            <OverrideURL />
+                            <Tags />
+                            <Times>
+                                <LastModificationTime>2018-10-17T14:25:04Z</LastModificationTime>
+                                <CreationTime>2018-10-17T14:24:51Z</CreationTime>
+                                <LastAccessTime>2018-10-17T14:24:51Z</LastAccessTime>
+                                <ExpiryTime>4001-01-01T00:00:00Z</ExpiryTime>
+                                <Expires>False</Expires>
+                                <UsageCount>0</UsageCount>
+                                <LocationChanged>2018-10-17T14:24:51Z</LocationChanged>
+                            </Times>
+                            <String>
+                                <Key>Title</Key>
+                                <Value>Entry2</Value>
+                            </String>
+                            <String>
+                                <Key>UserName</Key>
+                                <Value />
+                            </String>
+                            <String>
+                                <Key>Password</Key>
+                                <Value Protected="True">NTFH7lFk</Value>
+                            </String>
+                            <String>
+                                <Key>URL</Key>
+                                <Value />
+                            </String>
+                            <String>
+                                <Key>Notes</Key>
+                                <Value />
+                            </String>
+                            <AutoType>
+                                <Enabled>True</Enabled>
+                                <DataTransferObfuscation>0</DataTransferObfuscation>
+                            </AutoType>
+                        </Entry>
+                    </History>
+                </Entry>
+                <Entry>
+                    <UUID>c7ZGVQBjT3KDdPEuuFviqQ==</UUID>
+                    <IconID>0</IconID>
+                    <ForegroundColor />
+                    <BackgroundColor />
+                    <OverrideURL />
+                    <Tags />
+                    <Times>
+                        <LastModificationTime>2018-10-17T18:41:02Z</LastModificationTime>
+                        <CreationTime>2018-10-17T18:40:56Z</CreationTime>
+                        <LastAccessTime>2018-10-17T18:40:56Z</LastAccessTime>
+                        <ExpiryTime>4001-01-01T00:00:00Z</ExpiryTime>
+                        <Expires>False</Expires>
+                        <UsageCount>0</UsageCount>
+                        <LocationChanged>2018-10-17T18:40:56Z</LocationChanged>
+                    </Times>
+                    <String>
+                        <Key>Title</Key>
+                        <Value>Entry 3</Value>
+                    </String>
+                    <String>
+                        <Key>UserName</Key>
+                        <Value />
+                    </String>
+                    <String>
+                        <Key>Password</Key>
+                        <Value Protected="True">Tw6tVG1Z</Value>
+                    </String>
+                    <String>
+                        <Key>URL</Key>
+                        <Value />
+                    </String>
+                    <String>
+                        <Key>Notes</Key>
+                        <Value />
+                    </String>
+                    <AutoType>
+                        <Enabled>True</Enabled>
+                        <DataTransferObfuscation>0</DataTransferObfuscation>
+                    </AutoType>
+                    <History>
+                        <Entry>
+                            <UUID>c7ZGVQBjT3KDdPEuuFviqQ==</UUID>
+                            <IconID>0</IconID>
+                            <ForegroundColor />
+                            <BackgroundColor />
+                            <OverrideURL />
+                            <Tags />
+                            <Times>
+                                <LastModificationTime>2018-10-17T18:40:56Z</LastModificationTime>
+                                <CreationTime>2018-10-17T18:40:56Z</CreationTime>
+                                <LastAccessTime>2018-10-17T18:40:56Z</LastAccessTime>
+                                <ExpiryTime>4001-01-01T00:00:00Z</ExpiryTime>
+                                <Expires>False</Expires>
+                                <UsageCount>0</UsageCount>
+                                <LocationChanged>2018-10-17T18:40:56Z</LocationChanged>
+                            </Times>
+                            <String>
+                                <Key>Title</Key>
+                                <Value>New Entry</Value>
+                            </String>
+                            <String>
+                                <Key>UserName</Key>
+                                <Value />
+                            </String>
+                            <String>
+                                <Key>Password</Key>
+                                <Value Protected="True">1evsOZnQVoFTqPwq</Value>
+                            </String>
+                            <String>
+                                <Key>URL</Key>
+                                <Value />
+                            </String>
+                            <String>
+                                <Key>Notes</Key>
+                                <Value />
+                            </String>
+                            <AutoType>
+                                <Enabled>True</Enabled>
+                                <DataTransferObfuscation>0</DataTransferObfuscation>
+                            </AutoType>
+                        </Entry>
+                    </History>
+                </Entry>
+                <Entry>
+                    <UUID>5FrhiZ4JTf6AiPp2Wvgzgg==</UUID>
+                    <IconID>0</IconID>
+                    <ForegroundColor />
+                    <BackgroundColor />
+                    <OverrideURL />
+                    <Tags />
+                    <Times>
+                        <LastModificationTime>2018-10-17T19:01:26Z</LastModificationTime>
+                        <CreationTime>2018-10-17T19:01:13Z</CreationTime>
+                        <LastAccessTime>2018-10-17T19:01:13Z</LastAccessTime>
+                        <ExpiryTime>4001-01-01T00:00:00Z</ExpiryTime>
+                        <Expires>False</Expires>
+                        <UsageCount>0</UsageCount>
+                        <LocationChanged>2018-10-17T19:01:13Z</LocationChanged>
+                    </Times>
+                    <String>
+                        <Key>Title</Key>
+                        <Value>Entry 4</Value>
+                    </String>
+                    <String>
+                        <Key>UserName</Key>
+                        <Value />
+                    </String>
+                    <String>
+                        <Key>Password</Key>
+                        <Value Protected="True">Fs63JQ1BrBPhbYpp</Value>
+                    </String>
+                    <String>
+                        <Key>URL</Key>
+                        <Value />
+                    </String>
+                    <String>
+                        <Key>Notes</Key>
+                        <Value />
+                    </String>
+                    <AutoType>
+                        <Enabled>True</Enabled>
+                        <DataTransferObfuscation>0</DataTransferObfuscation>
+                    </AutoType>
+                    <History>
+                        <Entry>
+                            <UUID>5FrhiZ4JTf6AiPp2Wvgzgg==</UUID>
+                            <IconID>0</IconID>
+                            <ForegroundColor />
+                            <BackgroundColor />
+                            <OverrideURL />
+                            <Tags />
+                            <Times>
+                                <LastModificationTime>2018-10-17T19:01:13Z</LastModificationTime>
+                                <CreationTime>2018-10-17T19:01:13Z</CreationTime>
+                                <LastAccessTime>2018-10-17T19:01:13Z</LastAccessTime>
+                                <ExpiryTime>4001-01-01T00:00:00Z</ExpiryTime>
+                                <Expires>False</Expires>
+                                <UsageCount>0</UsageCount>
+                                <LocationChanged>2018-10-17T19:01:13Z</LocationChanged>
+                            </Times>
+                            <String>
+                                <Key>Title</Key>
+                                <Value>New Entry</Value>
+                            </String>
+                            <String>
+                                <Key>UserName</Key>
+                                <Value />
+                            </String>
+                            <String>
+                                <Key>Password</Key>
+                                <Value Protected="True">4U9+0AWYGPgo0pHT</Value>
+                            </String>
+                            <String>
+                                <Key>URL</Key>
+                                <Value />
+                            </String>
+                            <String>
+                                <Key>Notes</Key>
+                                <Value />
+                            </String>
+                            <AutoType>
+                                <Enabled>True</Enabled>
+                                <DataTransferObfuscation>0</DataTransferObfuscation>
+                            </AutoType>
+                        </Entry>
+                    </History>
+                </Entry>
+                <Entry>
+                    <UUID>T9B/F4YoR0iqlIBmzZ6A8g==</UUID>
+                    <IconID>0</IconID>
+                    <ForegroundColor />
+                    <BackgroundColor />
+                    <OverrideURL />
+                    <Tags />
+                    <Times>
+                        <LastModificationTime>2018-10-17T19:01:34Z</LastModificationTime>
+                        <CreationTime>2018-10-17T19:01:28Z</CreationTime>
+                        <LastAccessTime>2018-10-17T19:01:28Z</LastAccessTime>
+                        <ExpiryTime>4001-01-01T00:00:00Z</ExpiryTime>
+                        <Expires>False</Expires>
+                        <UsageCount>0</UsageCount>
+                        <LocationChanged>2018-10-17T19:01:28Z</LocationChanged>
+                    </Times>
+                    <String>
+                        <Key>Title</Key>
+                        <Value>Entry5</Value>
+                    </String>
+                    <String>
+                        <Key>UserName</Key>
+                        <Value />
+                    </String>
+                    <String>
+                        <Key>Password</Key>
+                        <Value Protected="True">e58Q</Value>
+                    </String>
+                    <String>
+                        <Key>URL</Key>
+                        <Value />
+                    </String>
+                    <String>
+                        <Key>Notes</Key>
+                        <Value />
+                    </String>
+                    <AutoType>
+                        <Enabled>True</Enabled>
+                        <DataTransferObfuscation>0</DataTransferObfuscation>
+                    </AutoType>
+                    <History>
+                        <Entry>
+                            <UUID>T9B/F4YoR0iqlIBmzZ6A8g==</UUID>
+                            <IconID>0</IconID>
+                            <ForegroundColor />
+                            <BackgroundColor />
+                            <OverrideURL />
+                            <Tags />
+                            <Times>
+                                <LastModificationTime>2018-10-17T19:01:28Z</LastModificationTime>
+                                <CreationTime>2018-10-17T19:01:28Z</CreationTime>
+                                <LastAccessTime>2018-10-17T19:01:28Z</LastAccessTime>
+                                <ExpiryTime>4001-01-01T00:00:00Z</ExpiryTime>
+                                <Expires>False</Expires>
+                                <UsageCount>0</UsageCount>
+                                <LocationChanged>2018-10-17T19:01:28Z</LocationChanged>
+                            </Times>
+                            <String>
+                                <Key>Title</Key>
+                                <Value>New Entry</Value>
+                            </String>
+                            <String>
+                                <Key>UserName</Key>
+                                <Value />
+                            </String>
+                            <String>
+                                <Key>Password</Key>
+                                <Value Protected="True">gCutyEVhI7QA5blI</Value>
+                            </String>
+                            <String>
+                                <Key>URL</Key>
+                                <Value />
+                            </String>
+                            <String>
+                                <Key>Notes</Key>
+                                <Value />
+                            </String>
+                            <AutoType>
+                                <Enabled>True</Enabled>
+                                <DataTransferObfuscation>0</DataTransferObfuscation>
+                            </AutoType>
+                        </Entry>
+                    </History>
+                </Entry>
+            </Group>
+        </Group>
+        <DeletedObjects>
+            <DeletedObject>
+                <UUID>OjDGBkygSXyeOh33r/aDJQ==</UUID>
+                <DeletionTime>2018-10-17T14:06:27Z</DeletionTime>
+            </DeletedObject>
+        </DeletedObjects>
+    </Root>
+</KeePassFile>
+

+ 0 - 14
Package.swift

@@ -79,19 +79,5 @@ let package = Package(
         .target(
             name: "Twofish",
             dependencies: []),
-        
-        // MARK: Test Targets
-
-        .testTarget(
-            name: "KeePassTests",
-            dependencies: ["KeePass"]),
-
-        .testTarget(
-            name: "BinaryTests",
-            dependencies: ["Binary"]),
-
-        .testTarget(
-            name: "CryptoTests",
-            dependencies: ["Crypto"]),
     ]
 )

+ 18 - 1
README.md

@@ -1,3 +1,20 @@
 # KeePass
 
-A description of this package.
+KeePass library written in **Swift**.
+
+⚠️ This project is under construction. ⚠️
+
+## Dependencies
+
+We use the following libraries as dependencies inside of KeePass:
+- [Sodium](https://github.com/jedisct1/libsodium)
+- [Argon2](https://github.com/P-H-C/phc-winner-argon2)
+- [Twofish](Sources/Twofish)
+- [GzipSwift](https://github.com/1024jp/GzipSwift)
+- [AEXML](https://github.com/tadija/AEXML)
+
+## License
+
+Copyright © 2020 Maxime Epain.
+                                               
+KeePass.swift is licensed under the GNU General Public License v3.0. See the [LICENSE](LICENSE) file for more info.

+ 17 - 3
Sources/KDB/Entry.swift

@@ -64,22 +64,36 @@ public final class Entry: Row, Streamable {
 
     var parent: Group?
 
-    public var fields: [Field<Type>]
+    public var properties: [Property<Type>]
 
     public required init() {
-        fields = []
+        properties = []
     }    
 }
 
 extension Entry {
 
+    public var creationDate: Date {
+        date(at: .creationTime) ?? Date.distantPast
+    }
+
+    public var lastModifiedDate: Date {
+        get { date(at: .lastModifiedTime) ?? Date.distantPast }
+        set { set(newValue, at: .lastModifiedTime) }
+    }
+
+    public var lastAccessDate: Date {
+        get { date(at: .lastAccessTime) ?? Date.distantPast }
+        set { set(newValue, at: .lastAccessTime) }
+    }
+
     var isMetaEntry: Bool {
         return false
     }
 
     public func removeFromParent() {
         parent?.entries.removeAll(where: { $0 == self })
-        fields.removeAll(.groupID)
+        self[.groupID] = -1
     }
 }
 

+ 34 - 3
Sources/KDB/Group.swift

@@ -39,14 +39,14 @@ public final class Group: Row, Streamable {
 
     var parent: Group?
 
-    public var fields: [Field<Type>]
+    public var properties: [Property<Type>]
 
     public var childs: [Group]
 
     public var entries: [Entry]
 
     public required init() {
-        fields = []
+        properties = []
         childs = []
         entries = []
     }
@@ -54,6 +54,35 @@ public final class Group: Row, Streamable {
 
 extension Group {
 
+    public var name: String {
+        get { self[.name] ?? "" }
+        set { self[.name] = newValue }
+    }
+
+    public var level: Int {
+        get { self[.groupLevel] ?? 0 }
+        set { self[.groupLevel] = newValue }
+    }
+
+    public var icon: Int {
+        get { self[.iconID] ?? 0 }
+        set { self[.iconID] = newValue }
+    }
+
+    public var creationDate: Date {
+        date(at: .creationTime) ?? Date.distantPast
+    }
+
+    public var lastModifiedDate: Date {
+        get { date(at: .lastModifiedTime) ?? Date.distantPast }
+        set { set(newValue, at: .lastModifiedTime) }
+    }
+
+    public var lastAccessDate: Date {
+        get { date(at: .lastAccessTime) ?? Date.distantPast }
+        set { set(newValue, at: .lastAccessTime) }
+    }
+
     public func removeFromParent() {
         parent?.childs.removeAll(where: { $0 == self })
     }
@@ -67,6 +96,7 @@ extension Group {
     public func add(_ group: Group) {
         group.removeFromParent()
         childs.append(group)
+        group.level = level + 1
     }
 }
 
@@ -78,8 +108,9 @@ extension Group: Hashable {
     }
 
     public func hash(into hasher: inout Hasher) {
+        if let groupID = self[.groupID] { hasher.combine(groupID) }
+        if let name = self[.name] { hasher.combine(name) }
         if let groupLevel = self[.groupLevel] { hasher.combine(groupLevel) }
-        if let groupFlags = self[.groupFlags] { hasher.combine(groupFlags) }
     }
 
 }

+ 23 - 14
Sources/KDB/Row.swift

@@ -19,7 +19,7 @@
 import Foundation
 import Binary
 
-public typealias Field<Type> = TLV<Type, UInt32>
+public typealias Property<Type> = TLV<Type, UInt32>
 
 public protocol Row: class {
 
@@ -27,7 +27,7 @@ public protocol Row: class {
 
     static var End: Type { get }
 
-    var fields: [Field<Type>] { get set }
+    var properties: [Property<Type>] { get set }
 
     init()
 }
@@ -35,18 +35,27 @@ public protocol Row: class {
 extension Row {
 
     public subscript(_ type: Type) -> Bytes? {
-        get { fields.first(where: { $0.type == type })?.value }
+        get { properties.first(where: { $0.type == type })?.value }
         set {
-            fields.removeAll(where: { $0.type == type })
+            properties.removeAll(type)
             guard let value = newValue else { return }
-            let tlv = Field(type: type, value: value)
-            fields.insert(tlv, at: 0)
+            let tlv = Property(type: type, value: value)
+            properties.insert(tlv, at: 0)
         }
     }
 
-    public func set(_ field: Field<Type>) {
-        fields.removeAll(where: { $0.type == field.type })
-        fields.insert(field, at: 0)
+    public func set(_ field: Property<Type>) {
+        properties.removeAll(field.type)
+        properties.insert(field, at: 0)
+    }
+
+    public func set(_ date: Date, at type: Type) {
+        self[type] = Database.bytes(from: date)
+    }
+
+    public func date(at type: Type) -> Date? {
+        guard let bytes = self[type] else { return nil }
+        return Database.date(from: bytes)
     }
 
     public subscript<T>(_ type: Type) -> T? where T: BytesRepresentable {
@@ -58,7 +67,7 @@ extension Row {
     }
 
     public func remove(_ type: Type) {
-        fields.removeAll(where: { $0.type == type })
+        properties.removeAll(type)
     }
 }
 
@@ -67,9 +76,9 @@ extension Readable where Self: Row {
     public init(from input: Input) throws {
         self.init()
         while true {
-            let field = try input.read() as Field<Type>
+            let field = try input.read() as Property<Type>
             guard field.type != Self.End else { break }
-            fields.append(field)
+            properties.append(field)
         }
     }
 
@@ -78,8 +87,8 @@ extension Readable where Self: Row {
 extension Writable where Self: Row {
 
     public func write(to output: Output) throws {
-        try output.write(fields)
-        let end = Field(type: Self.End, value: [])
+        try output.write(properties)
+        let end = Property(type: Self.End, value: [])
         try output.write(end)
     }
     

+ 40 - 0
Sources/KDBX/Database0.swift

@@ -0,0 +1,40 @@
+// Database0.swift
+// This file is part of KeePass.
+//
+// Copyright © 2019 ___ORGANIZATIONNAME___. All rights reserved.
+//
+// KeePass 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 3 of the License, or
+// (at your option) any later version.
+//
+// KeePass 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 KeePass. If not, see <https://www.gnu.org/licenses/>.
+
+import Foundation
+import Binary
+import XML
+
+class Database0: Database {
+
+    let document: Document
+
+    required init(from input: Input) throws {
+        var options = XML.Options()
+        options.parserSettings.shouldTrimWhitespace = false
+        document = try XML.Document(xml: input.bytes.data, options: options)
+    }
+
+}
+
+extension Database0: Writable {
+
+    func write(to output: Output) throws {
+        try output.write(document.xml)
+    }
+}

+ 11 - 0
Sources/KDBX/File.swift

@@ -36,6 +36,11 @@ public class File {
 
     public let database: Database & Writable
 
+    public required init(from input: Input) throws {
+        version = Version(major: 0, minor: 0)
+        database = try Database0(from: input)
+    }
+
     public required init(from input: Input, compositeKey: CompositeKey) throws {
         version = try input.read()
 
@@ -46,6 +51,12 @@ public class File {
         }
     }
 
+    public convenience init(xml file: URL) throws {
+        let bytes = try Bytes(contentsOf: file)
+        let stream = Input(bytes: bytes)
+        try self.init(from: stream)
+    }
+
     public convenience init(from file: URL, compositeKey: CompositeKey) throws {
 
         let bytes = try Bytes(contentsOf: file)

+ 6 - 6
Sources/KDBX/KeyDerivation.swift

@@ -32,8 +32,8 @@ extension AESKeyDerivation {
 
     convenience init(parameters: [String: Variant]) throws {
         guard
-            let seed = parameters[AESKeyDerivation.TransformSeedKey],
-            let rounds = parameters[AESKeyDerivation.TransformRoundsKey]
+            let seed    = parameters[AESKeyDerivation.TransformSeedKey],
+            let rounds  = parameters[AESKeyDerivation.TransformRoundsKey]
         else { throw KDBXError.corruptedDatabase }
 
         try self.init(seed: try seed.unwrap(),
@@ -54,11 +54,11 @@ extension Argon2 {
     
     convenience init(parameters: [String: Variant]) throws {
         guard
-            let salt = parameters[Argon2.SaltKey],
+            let salt        = parameters[Argon2.SaltKey],
             let parallelism = parameters[Argon2.ParallelismKey],
-            let memory = parameters[Argon2.MemoryKey],
-            let iterations = parameters[Argon2.IterationsKey],
-            let version = parameters[Argon2.VersionKey]
+            let memory      = parameters[Argon2.MemoryKey],
+            let iterations  = parameters[Argon2.IterationsKey],
+            let version     = parameters[Argon2.VersionKey]
         else { throw KDBXError.corruptedDatabase }
 
         self.init(salt:         try salt.unwrap(),

+ 11 - 4
Sources/KeePass/Entry.swift

@@ -24,13 +24,20 @@ public let EntryFieldPassword = "Password"
 public let EntryFieldURL      = "URL"
 public let EntryFieldNotes    = "Notes"
 
-public protocol Entry: RandomAccessCollection where Element == Field, Index == Int {
-    mutating func set(_ field: Element)
+public protocol Entry {
+
+    associatedtype Fields: RandomAccessCollection where Fields.Element == Field
+
+    var times: Timestamp { get }
+
+    var fields: Fields { get }
+    
+    mutating func set(_ field: Fields.Element)
 }
 
 extension Entry {
 
-    subscript(_ field: String) -> Element? {
-        return first(where: { $0.name == field })
+    subscript(_ field: String) -> Fields.Element? {
+        return fields.first(where: { $0.name == field })
     }
 }

+ 9 - 1
Sources/KeePass/Field.swift

@@ -23,5 +23,13 @@ public struct Field {
     public var name: String
     public var value: String?
     public var isProtected: Bool
-    public var isReadeOnly: Bool
 }
+
+public struct Times {
+    public let creationDate: Date
+    public var lastModifiedDate: Date
+    public var lastAccessDate: Date
+    public var expirationDate: Date?
+}
+
+

+ 20 - 35
Sources/KeePass/KDB.swift

@@ -24,7 +24,7 @@ extension KDB.Database: Database {
 
 }
 
-extension KDB.Field where Type == KDB.Entry.`Type` {
+extension KDB.Property where Type == KDB.Entry.`Type` {
 
     init?(_ field: Field) {
 
@@ -53,13 +53,8 @@ extension KDB.Field where Type == KDB.Entry.`Type` {
 extension KDB.Group: Group {
 
     public var title: String {
-        get { self[.name] ?? "" }
-        set { self[.name] = newValue }
-    }
-
-    public var icon: Int {
-        get { self[.iconID] ?? 0 }
-        set { self[.iconID] = newValue }
+        get { name }
+        set { name = newValue }
     }
 
     public var groups: [KDB.Group] { childs }
@@ -67,60 +62,50 @@ extension KDB.Group: Group {
 
 extension KDB.Entry: Entry {
 
-    public subscript(position: Int) -> Field {
-        Field( fields[position] )
+    public var times: Timestamp {
+         return self
     }
-
-    public var startIndex: Int {
-        fields.startIndex
+    
+    public var fields: [Field] {
+        properties.compactMap { Field($0) }
     }
 
-    public var endIndex: Int {
-        fields.endIndex
+    public func set(_ field: Field) {
+        guard let field = KDB.Property(field) else { return }
+        set(field)
     }
 
-    public func index(after i: Int) -> Int {
-        fields.index(after: i)
-    }
+}
 
-    public func set(_ field: Field) {
-        guard let field = KDB.Field(field) else { return }
-        set(field)
+extension KDB.Entry: Timestamp {
+
+    public var expirationDate: Date? {
+        get { nil }
+        set { }
     }
 
 }
 
 extension Field {
 
-    init(_ field: KDB.Field<KDB.Entry.`Type`>) {
+    init?(_ field: KDB.Property<KDB.Entry.`Type`>) {
 
         switch field.type {
         case .title:
             name = EntryFieldTitle
-            isReadeOnly = false
-            isProtected = false
         case .url:
             name = EntryFieldURL
-            isReadeOnly = false
-            isProtected = false
         case .username:
             name = EntryFieldUserName
-            isReadeOnly = false
-            isProtected = false
         case .password:
             name = EntryFieldPassword
-            isReadeOnly = false
-            isProtected = true
         case .notes:
             name = EntryFieldNotes
-            isReadeOnly = false
-            isProtected = false
         default:
-            name = ""
-            isReadeOnly = true
-            isProtected = false
+            return nil
         }
 
         value = try? field.get()
+        isProtected = field.type == .password
     }
 }

+ 40 - 16
Sources/KeePass/KDBX.swift

@@ -21,8 +21,10 @@ import Binary
 import XML
 import KDBX
 
+let DateFormatter = ISO8601DateFormatter()
+
 extension KDBX.File: Database {
-    public var root: Element { database.document.root }
+    public var root: Element { database.document.root.KeePassFile.Root }
 }
 
 extension Element {
@@ -39,37 +41,59 @@ extension XML.Element {
 
 extension Field {
 
-    init(_ element: XML.Element) {
-        name = element.name
-        value = element.value
-        isProtected = false
-        isReadeOnly = false
+    init?(_ element: XML.Element) {
+        guard let key = element.Key.value else { return nil}
+        name = key
+
+        value = element.Value.value
+        isProtected = element.Value.attributes["Protected"] == "True"
     }
 }
 
 extension XML.Element: Entry {
 
+    public var times: Timestamp { self.Times }
+
+    public var fields: [Field] {
+        allDescendants(where: { $0.name == "String" }).compactMap { Field($0) }
+    }
+
     public func set(_ field: Field) {
         allDescendants(where: { $0.name == field.name })
             .forEach { $0.removeFromParent() }
         addChild( XML.Element(field) )
     }
+}
+
+extension XML.Element: Timestamp {
 
-    public subscript(position: Int) -> Field {
-        Field( children[position] )
+    public var creationDate: Date {
+        self.CreationTime.date(formatter: DateFormatter) ?? .distantPast
     }
 
-    public func index(after i: Int) -> Int {
-        children.index(after: i)
+    public var lastModifiedDate: Date {
+        get { self.LastModificationTime.date(formatter: DateFormatter) ?? .distantPast }
+        set { self.LastModificationTime.value = DateFormatter.string(from: newValue) }
     }
 
-    public var startIndex: Int {
-        children.startIndex
+    public var lastAccessDate: Date {
+        get { self.LastAccessTime.date(formatter: DateFormatter) ?? .distantPast }
+        set { self.LastAccessTime.value = DateFormatter.string(from: newValue) }
     }
 
-    public var endIndex: Int {
-        children.endIndex
+    public var expirationDate: Date? {
+        get { self.ExpiryTime.date(formatter: DateFormatter) }
+        set {
+            if let value = newValue {
+                self.ExpiryTime.value = DateFormatter.string(from: value)
+                addChild(name: "Expires", value: "True")
+            } else {
+                self.ExpiryTime.value = DateFormatter.string(from: Date.distantFuture)
+                addChild(name: "Expires", value: "False")
+            }
+        }
     }
+
 }
 
 extension XML.Element: Group {
@@ -88,11 +112,11 @@ extension XML.Element: Group {
     }
 
     public var entries: [Element] {
-        allDescendants(where: { $0.name == "Entry"})
+        allDescendants(where: { $0.name == "Entry" })
     }
 
     public var groups: [Element] {
-        allDescendants(where: { $0.name == "Group"})
+        allDescendants(where: { $0.name == "Group" })
     }
 
 }

+ 4 - 0
Sources/KeePass/KeePass.swift

@@ -50,4 +50,8 @@ public class KeePass {
         }
     }
 
+    public static func open(contentOf xml: URL) throws -> AnyDatabase {
+        return AnyDatabase ( try KDBX.File(xml: xml) )
+    }
+
 }

+ 26 - 0
Sources/KeePass/Timestamp.swift

@@ -0,0 +1,26 @@
+// Timestamp.swift
+// This file is part of KeePass.
+//
+// Copyright © 2019 Maxime Epain. All rights reserved.
+//
+// KeePass 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 3 of the License, or
+// (at your option) any later version.
+//
+// KeePass 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 KeePass. If not, see <https://www.gnu.org/licenses/>.
+
+import Foundation
+
+public protocol Timestamp {
+    var creationDate: Date { get }
+    var lastModifiedDate: Date { get set }
+    var lastAccessDate: Date { get set }
+    var expirationDate: Date? { get set }
+}

+ 11 - 70
Sources/KeePass/TypeErasure.swift

@@ -32,20 +32,15 @@ internal class _AnyDatabaseBoxBase: Database {
 internal final class _AnyDatabaseBox<Base>: _AnyDatabaseBoxBase where Base: Database {
     internal override var root: AnyGroup { AnyGroup( _base.root ) }
     internal var _base: Base
-    internal init(_ base: Base) {
-        self._base = base
-    }
+    internal init(_ base: Base) { _base = base }
 }
 
 public class AnyDatabase: Database {
-
     public var root: AnyGroup { _box.root }
-
     internal let _box: _AnyDatabaseBoxBase
     internal init<T>(_ base: T) where T: Database {
         _box = _AnyDatabaseBox(base)
     }
-    
 }
 
 // MARK: - Group
@@ -82,9 +77,7 @@ internal final class _AnyGroupBox<Base>: _AnyGroupBoxBase where Base: Group {
     internal override var groups: AnyRandomAccessCollection<AnyGroup> { AnyRandomAccessCollection<AnyGroup>(_base.groups.map { AnyGroup($0) }) }
 
     internal var _base: Base
-    internal init(_ base: Base) {
-        self._base = base
-    }
+    internal init(_ base: Base) { _base = base }
 }
 
 public class AnyGroup: Group {
@@ -106,82 +99,30 @@ public class AnyGroup: Group {
     internal init<T>(_ base: T) where T: Group {
         _box = _AnyGroupBox(base)
     }
-
 }
 
 // MARK: - Entry
 
 internal class _AnyEntryBoxBase: Entry {
-    internal subscript(position: Int) -> Field { _abstract() }
-    internal var startIndex: Int { _abstract() }
-    internal var endIndex: Int { _abstract() }
-    internal func index(after i: Int) -> Int { _abstract() }
-    internal func index(before i: Int) -> Int { _abstract() }
+    internal var times: Timestamp { _abstract() }
+    internal var fields: AnyRandomAccessCollection<Field> { _abstract() }
     internal func set(_ field: Field) { _abstract() }
 }
 
 internal final class _AnyEntryBox<Base>: _AnyEntryBoxBase where Base: Entry {
-
-    internal override subscript(position: Int) -> Element {
-        _base[position]
-    }
-
-    internal override var startIndex: Int {
-        _base.startIndex
-    }
-
-    internal override var endIndex: Int {
-        _base.endIndex
-    }
-
-    internal override func index(after i: Int) -> Int {
-        _base.index(after: i)
-    }
-
-    internal override func index(before i: Int) -> Int {
-        _base.index(before: i)
-    }
-
-    internal override func set(_ field: Element) {
-        _base.set(field)
-    }
-
+    internal override var times: Timestamp { _base.times }
+    internal override var fields: AnyRandomAccessCollection<Field> { AnyRandomAccessCollection<Field>(_base.fields) }
+    internal override func set(_ field: Field) { _base.set(field) }
     internal var _base: Base
-    internal init(_ base: Base) {
-        self._base = base
-    }
-
+    internal init(_ base: Base) { _base = base }
 }
 
 public final class AnyEntry: Entry {
-
-    public subscript(position: Int) -> Field {
-        _box[position]
-    }
-
-    public var startIndex: Int {
-        _box.startIndex
-    }
-
-    public var endIndex: Int {
-        _box.endIndex
-    }
-
-    public func index(after i: Int) -> Int {
-        _box.index(after: i)
-    }
-
-    public func index(before i: Int) -> Int {
-        _box.index(before: i)
-    }
-
-    public func set(_ field: Field) {
-        _box.set(field)
-    }
-
+    public var times: Timestamp { _box.times }
+    public var fields: AnyRandomAccessCollection<Field> { _box.fields }
+    public func set(_ field: Field) { _box.set(field) }
     internal let _box: _AnyEntryBoxBase
     internal init<T>(_ base: T) where T: Entry {
         _box = _AnyEntryBox(base)
     }
-
 }

+ 11 - 1
Sources/XML/Element.swift

@@ -69,13 +69,23 @@ open class Element {
     }
 
     public subscript(dynamicMember member: String) -> Element {
-        return self[member]
+        self[member]
     }
 
     open func get<T>() throws -> T where T: LosslessStringConvertible {
         guard let string = value, let value = T(string) else { throw XMLError.valueConversionFailed }
         return value
     }
+
+    open func date(formatter: DateFormatter) -> Date? {
+        guard let value = value else { return nil }
+        return formatter.date(from: value)
+    }
+
+    open func date(formatter: ISO8601DateFormatter = ISO8601DateFormatter()) -> Date? {
+        guard let value = value else { return nil }
+        return formatter.date(from: value)
+    }
     
     /// Returns all of the elements with equal name as `self` **(nil if not exists)**.
     open var all: [Element]? { return parent?.children.filter { $0.name == self.name } }

+ 84 - 0
Tests/CryptoTests/Chacha20Tests.swift

@@ -0,0 +1,84 @@
+// Chacha20Tests.swift
+// This file is part of KeePassKit.
+//
+// Copyright © 2019 Maxime Epain. All rights reserved.
+//
+// KeePassKit 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 3 of the License, or
+// (at your option) any later version.
+//
+// KeePassKit 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 KeePassKit. If not, see <https://www.gnu.org/licenses/>.
+
+import XCTest
+import Binary
+
+@testable import Crypto
+
+class Chacha20Tests: XCTestCase {
+
+    override func setUp() {
+        // Put setup code here. This method is called before the invocation of each test method in the class.
+    }
+
+    override func tearDown() {
+        // Put teardown code here. This method is called after the invocation of each test method in the class.
+    }
+
+    func testChaCha20() {
+        let keys: [Array<UInt8>] = [
+            [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],
+            [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, 0x01],
+            [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],
+            [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],
+            [0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f],
+        ]
+
+        let ivs: [Array<UInt8>] = [
+            [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, 0x01, 0x00, 0x00, 0x00, 0x00],
+            [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
+            [0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x00, 0x00, 0x00],
+        ]
+
+        let messages = [
+            "76B8E0ADA0F13D90405D6AE55386BD28BDD219B8A08DED1AA836EFCC8B770DC7DA41597C5157488D7724E03FB8D84A376A43B8F41518A11CC387B669B2EE6586",
+
+            "4540F05A9F1FB296D7736E7B208E3C96EB4FE1834688D2604F450952ED432D41BBE2A0B6EA7566D2A5D1E7E20D42AF2C53D792B1C43FEA817E9AD275AE546963",
+            "DE9CBA7BF3D69EF5E786DC63973F653A0B49E015ADBFF7134FCB7DF137821031E85A050278A7084527214F73EFC7FA5B5277062EB7A0433E445F41E3",
+            "EF3FDFD6C61578FBF5CF35BD3DD33B8009631634D21E42AC33960BD138E50D32111E4CAF237EE53CA8AD6426194A88545DDC497A0B466E7D6BBDB0041B2F586B",
+            "F798A189F195E66982105FFB640BB7757F579DA31602FC93EC01AC56F85AC3C134A4547B733B46413042C9440049176905D3BE59EA1C53F15916155C2BE8241A38008B9A26BC35941E2444177C8ADE6689DE95264986D95889FB60E84629C9BD9A5ACB1CC118BE563EB9B3A4A472F82E09A7E778492B562EF7130E88DFE031C79DB9D4F7C7A899151B9A475032B63FC385245FE054E3DD5A97A5F576FE064025D3CE042C566AB2C507B138DB853E3D6959660996546CC9C4A6EAFDC777C040D70EAF46F76DAD3979E5C5360C3317166A1C894C94A371876A94DF7628FE4EAAF2CCB27D5AAAE0AD7AD0F9D4B6AD3B54098746D4524D38407A6DEB3AB78FAB78C9",
+        ]
+
+        for index in 0..<keys.count {
+
+            let key = Bytes(rawValue: keys[index])
+            let iv = Bytes(rawValue: ivs[index])
+
+            let data = Bytes(string: messages[index], using: .utf8)!
+
+            do {
+                let encrypted = try ChaCha20(key: key, iv: iv).encrypt(data: data)
+                let decrypted = try ChaCha20(key: key, iv: iv).decrypt(data: encrypted)
+
+                let message = String(bytes: decrypted, encoding: .utf8)
+                XCTAssertEqual(message, messages[index], "ChaCha20 decryption failed")
+
+            } catch {
+                XCTAssert(false, "Failed")
+            }
+        }
+    }
+
+    static var allTests = [
+        ("testChaCha20", testChaCha20),
+    ]
+
+}

+ 0 - 13
Tests/CryptoTests/CryptoTests.swift

@@ -1,13 +0,0 @@
-import XCTest
-@testable import Crypto
-
-final class CryptoTests: XCTestCase {
-
-    func testExample() {
-        
-    }
-
-    static var allTests = [
-        ("testExample", testExample),
-    ]
-}

+ 83 - 0
Tests/CryptoTests/Salsa20Tests.swift

@@ -0,0 +1,83 @@
+// Salsa20Tests.swift
+// This file is part of KeePassKit.
+//
+// Copyright © 2019 Maxime Epain. All rights reserved.
+//
+// KeePassKit 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 3 of the License, or
+// (at your option) any later version.
+//
+// KeePassKit 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 KeePassKit. If not, see <https://www.gnu.org/licenses/>.
+
+import XCTest
+import Binary
+
+@testable import Crypto
+
+class Salsa20Tests: XCTestCase {
+
+    override func setUp() {
+        // Put setup code here. This method is called before the invocation of each test method in the class.
+    }
+
+    override func tearDown() {
+        // Put teardown code here. This method is called after the invocation of each test method in the class.
+    }
+
+    func testSalsa20() {
+        let keys: [Array<UInt8>] = [
+            [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],
+            [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, 0x01],
+            [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],
+            [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],
+            [0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f],
+        ]
+
+        let ivs: [Array<UInt8>] = [
+            [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
+            [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
+            [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01],
+            [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
+            [0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07],
+        ]
+
+        let messages = [
+            "76B8E0ADA0F13D90405D6AE55386BD28BDD219B8A08DED1AA836EFCC8B770DC7DA41597C5157488D7724E03FB8D84A376A43B8F41518A11CC387B669B2EE6586",
+            "4540F05A9F1FB296D7736E7B208E3C96EB4FE1834688D2604F450952ED432D41BBE2A0B6EA7566D2A5D1E7E20D42AF2C53D792B1C43FEA817E9AD275AE546963",
+            "DE9CBA7BF3D69EF5E786DC63973F653A0B49E015ADBFF7134FCB7DF137821031E85A050278A7084527214F73EFC7FA5B5277062EB7A0433E445F41E3",
+            "EF3FDFD6C61578FBF5CF35BD3DD33B8009631634D21E42AC33960BD138E50D32111E4CAF237EE53CA8AD6426194A88545DDC497A0B466E7D6BBDB0041B2F586B",
+            "F798A189F195E66982105FFB640BB7757F579DA31602FC93EC01AC56F85AC3C134A4547B733B46413042C9440049176905D3BE59EA1C53F15916155C2BE8241A38008B9A26BC35941E2444177C8ADE6689DE95264986D95889FB60E84629C9BD9A5ACB1CC118BE563EB9B3A4A472F82E09A7E778492B562EF7130E88DFE031C79DB9D4F7C7A899151B9A475032B63FC385245FE054E3DD5A97A5F576FE064025D3CE042C566AB2C507B138DB853E3D6959660996546CC9C4A6EAFDC777C040D70EAF46F76DAD3979E5C5360C3317166A1C894C94A371876A94DF7628FE4EAAF2CCB27D5AAAE0AD7AD0F9D4B6AD3B54098746D4524D38407A6DEB3AB78FAB78C9",
+        ]
+
+        for index in 0..<keys.count {
+
+            let key = Bytes(rawValue: keys[index])
+            let iv = Bytes(rawValue: ivs[index])
+
+            let data = Bytes(string: messages[index], using: .utf8)!
+
+            do {
+                let encrypted = try Salsa20(key: key, iv: iv).encrypt(data: data)
+                let decrypted = try Salsa20(key: key, iv: iv).decrypt(data: encrypted)
+
+                let message = String(bytes: decrypted, encoding: .utf8)
+                XCTAssertEqual(message, messages[index], "Salsa20 decryption failed")
+
+            } catch {
+                XCTAssert(false, "Failed")
+            }
+        }
+    }
+
+    static var allTests = [
+        ("testSalsa20", testSalsa20),
+    ]
+
+}

+ 338 - 0
Tests/CryptoTests/TwofishTests.swift

@@ -0,0 +1,338 @@
+// TwofishTests.swift
+// This file is part of KeePassKit.
+//
+// Copyright © 2019 Maxime Epain. All rights reserved.
+//
+// KeePassKit 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 3 of the License, or
+// (at your option) any later version.
+//
+// KeePassKit 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 KeePassKit. If not, see <https://www.gnu.org/licenses/>.
+
+import XCTest
+import Binary
+
+@testable import Crypto
+
+class TwofishTests: XCTestCase {
+
+    override func setUp() {
+        // Put setup code here. This method is called before the invocation of each test method in the class.
+    }
+
+    override func tearDown() {
+        // Put teardown code here. This method is called after the invocation of each test method in the class.
+    }
+
+//    func testKey128_1() throws {
+//        let key = Bytes(hex: "00000000000000000000000000000000")!
+//        var pt = Bytes(hex: "00000000000000000000000000000000")!
+//        let ct = try Twofish(key: key).encrypt(data: pt)
+//        XCTAssertEqual(ct.hexa.uppercased(), "9F589F5CF6122C32B6BFEC2F2AE8C35A", "Twofish: Encrypt Keysize=128 I=1 failed")
+//
+//        pt = try Twofish(key: key).decrypt(data: ct)
+//        XCTAssertEqual(pt.hexa.uppercased(), "00000000000000000000000000000000", "Twofish: Decrypt Keysize=128 I=1 failed")
+//    }
+//
+//    func testKey128_2() throws {
+//        let key = Bytes(hex: "00000000000000000000000000000000")!
+//        var pt = Bytes(hex: "9F589F5CF6122C32B6BFEC2F2AE8C35A")!
+//        let ct = try Twofish(key: key).encrypt(data: pt)
+//        XCTAssertEqual(ct.hexa.uppercased(), "D491DB16E7B1C39E86CB086B789F5419", "Twofish: Encrypt Keysize=128 I=2 failed")
+//
+//        pt = try Twofish(key: key).decrypt(data: ct)
+//        XCTAssertEqual(pt.hexa.uppercased(), "9F589F5CF6122C32B6BFEC2F2AE8C35A", "Twofish: Decrypt Keysize=128 I=2 failed")
+//    }
+//
+//    func testKey128_3() throws {
+//        let key = Bytes(hex: "9F589F5CF6122C32B6BFEC2F2AE8C35A")!
+//        var pt = Bytes(hex: "D491DB16E7B1C39E86CB086B789F5419")!
+//        let ct = try Twofish(key: key).encrypt(data: pt)
+//        XCTAssertEqual(ct.hexa.uppercased(), "019F9809DE1711858FAAC3A3BA20FBC3", "Twofish: Encrypt Keysize=128 I=3 failed")
+//
+//        pt = try Twofish(key: key).decrypt(data: ct)
+//        XCTAssertEqual(pt.hexa.uppercased(), "D491DB16E7B1C39E86CB086B789F5419", "Twofish: Decrypt Keysize=128 I=3 failed")
+//    }
+//
+//    func testKey128_4() throws {
+//        let key = Bytes(hex: "D491DB16E7B1C39E86CB086B789F5419")!
+//        var pt = Bytes(hex: "019F9809DE1711858FAAC3A3BA20FBC3")!
+//        let ct = try Twofish(key: key).encrypt(data: pt)
+//        XCTAssertEqual(ct.hexa.uppercased(), "6363977DE839486297E661C6C9D668EB", "Twofish: Encrypt Keysize=128 I=4 failed")
+//
+//        pt = try Twofish(key: key).decrypt(data: ct)
+//        XCTAssertEqual(pt.hexa.uppercased(), "019F9809DE1711858FAAC3A3BA20FBC3", "Twofish: Decrypt Keysize=128 I=4 failed")
+//    }
+//
+//    func testKey128_5() throws {
+//        let key = Bytes(hex: "019F9809DE1711858FAAC3A3BA20FBC3")!
+//        var pt = Bytes(hex: "6363977DE839486297E661C6C9D668EB")!
+//        let ct = try Twofish(key: key).encrypt(data: pt)
+//        XCTAssertEqual(ct.hexa.uppercased(), "816D5BD0FAE35342BF2A7412C246F752", "Twofish: Encrypt Keysize=128 I=5 failed")
+//
+//        pt = try Twofish(key: key).decrypt(data: ct)
+//        XCTAssertEqual(pt.hexa.uppercased(), "6363977DE839486297E661C6C9D668EB", "Twofish: Decrypt Keysize=128 I=5 failed")
+//    }
+//
+//    func testKey128_6() throws {
+//        let key = Bytes(hex: "6363977DE839486297E661C6C9D668EB")!
+//        var pt = Bytes(hex: "816D5BD0FAE35342BF2A7412C246F752")!
+//        let ct = try Twofish(key: key).encrypt(data: pt)
+//        XCTAssertEqual(ct.hexa.uppercased(), "5449ECA008FF5921155F598AF4CED4D0", "Twofish: Encrypt Keysize=128 I=6 failed")
+//
+//        pt = try Twofish(key: key).decrypt(data: ct)
+//        XCTAssertEqual(pt.hexa.uppercased(), "816D5BD0FAE35342BF2A7412C246F752", "Twofish: Decrypt Keysize=128 I=6 failed")
+//    }
+//
+//    func testKey128_7() throws {
+//        let key = Bytes(hex: "816D5BD0FAE35342BF2A7412C246F752")!
+//        var pt = Bytes(hex: "5449ECA008FF5921155F598AF4CED4D0")!
+//        let ct = try Twofish(key: key).encrypt(data: pt)
+//        XCTAssertEqual(ct.hexa.uppercased(), "6600522E97AEB3094ED5F92AFCBCDD10", "Twofish: Encrypt Keysize=128 I=7 failed")
+//
+//        pt = try Twofish(key: key).decrypt(data: ct)
+//        XCTAssertEqual(pt.hexa.uppercased(), "5449ECA008FF5921155F598AF4CED4D0", "Twofish: Decrypt Keysize=128 I=7 failed")
+//    }
+//
+//    func testKey128_8() throws {
+//        let key = Bytes(hex: "5449ECA008FF5921155F598AF4CED4D0")!
+//        var pt = Bytes(hex: "6600522E97AEB3094ED5F92AFCBCDD10")!
+//        let ct = try Twofish(key: key).encrypt(data: pt)
+//        XCTAssertEqual(ct.hexa.uppercased(), "34C8A5FB2D3D08A170D120AC6D26DBFA", "Twofish: Encrypt Keysize=128 I=8 failed")
+//
+//        pt = try Twofish(key: key).decrypt(data: ct)
+//        XCTAssertEqual(pt.hexa.uppercased(), "6600522E97AEB3094ED5F92AFCBCDD10", "Twofish: Decrypt Keysize=128 I=8 failed")
+//    }
+//
+//    func testKey128_9() throws {
+//        let key = Bytes(hex: "6600522E97AEB3094ED5F92AFCBCDD10")!
+//        var pt = Bytes(hex: "34C8A5FB2D3D08A170D120AC6D26DBFA")!
+//        let ct = try Twofish(key: key).encrypt(data: pt)
+//        XCTAssertEqual(ct.hexa.uppercased(), "28530B358C1B42EF277DE6D4407FC591", "Twofish: Encrypt Keysize=128 I=9 failed")
+//
+//        pt = try Twofish(key: key).decrypt(data: ct)
+//        XCTAssertEqual(pt.hexa.uppercased(), "34C8A5FB2D3D08A170D120AC6D26DBFA", "Twofish: Decrypt Keysize=128 I=9 failed")
+//    }
+//
+//    func testKey128_10() throws {
+//        let key = Bytes(hex: "34C8A5FB2D3D08A170D120AC6D26DBFA")!
+//        var pt = Bytes(hex: "28530B358C1B42EF277DE6D4407FC591")!
+//        let ct = try Twofish(key: key).encrypt(data: pt)
+//        XCTAssertEqual(ct.hexa.uppercased(), "8A8AB983310ED78C8C0ECDE030B8DCA4", "Twofish: Encrypt Keysize=128 I=10 failed")
+//
+//        pt = try Twofish(key: key).decrypt(data: ct)
+//        XCTAssertEqual(pt.hexa.uppercased(), "28530B358C1B42EF277DE6D4407FC591", "Twofish: Decrypt Keysize=128 I=10 failed")
+//    }
+//
+//    func testKey192_1() throws {
+//        let key = Bytes(hex: "000000000000000000000000000000000000000000000000")!
+//        var pt = Bytes(hex: "00000000000000000000000000000000")!
+//        let ct = try Twofish(key: key).encrypt(data: pt)
+//        XCTAssertEqual(ct.hexa.uppercased(), "EFA71F788965BD4453F860178FC19101", "Twofish: Encrypt Keysize=192 I=1 failed")
+//
+//        pt = try Twofish(key: key).decrypt(data: ct)
+//        XCTAssertEqual(pt.hexa.uppercased(), "00000000000000000000000000000000", "Twofish: Decrypt Keysize=192 I=1 failed")
+//    }
+//
+//    func testKey192_2() throws {
+//        let key = Bytes(hex: "000000000000000000000000000000000000000000000000")!
+//        var pt = Bytes(hex: "EFA71F788965BD4453F860178FC19101")!
+//        let ct = try Twofish(key: key).encrypt(data: pt)
+//        XCTAssertEqual(ct.hexa.uppercased(), "88B2B2706B105E36B446BB6D731A1E88", "Twofish: Encrypt Keysize=192 I=2 failed")
+//
+//        pt = try Twofish(key: key).decrypt(data: ct)
+//        XCTAssertEqual(pt.hexa.uppercased(), "EFA71F788965BD4453F860178FC19101", "Twofish: Decrypt Keysize=192 I=2 failed")
+//    }
+//
+//    func testKey192_3() throws {
+//        let key = Bytes(hex: "EFA71F788965BD4453F860178FC191010000000000000000")!
+//        var pt = Bytes(hex: "88B2B2706B105E36B446BB6D731A1E88")!
+//        let ct = try Twofish(key: key).encrypt(data: pt)
+//        XCTAssertEqual(ct.hexa.uppercased(), "39DA69D6BA4997D585B6DC073CA341B2", "Twofish: Encrypt Keysize=192 I=3 failed")
+//
+//        pt = try Twofish(key: key).decrypt(data: ct)
+//        XCTAssertEqual(pt.hexa.uppercased(), "88B2B2706B105E36B446BB6D731A1E88", "Twofish: Decrypt Keysize=192 I=3 failed")
+//    }
+//
+//    func testKey192_4() throws {
+//        let key = Bytes(hex: "88B2B2706B105E36B446BB6D731A1E88EFA71F788965BD44")!
+//        var pt = Bytes(hex: "39DA69D6BA4997D585B6DC073CA341B2")!
+//        let ct = try Twofish(key: key).encrypt(data: pt)
+//        XCTAssertEqual(ct.hexa.uppercased(), "182B02D81497EA45F9DAACDC29193A65", "Twofish: Encrypt Keysize=192 I=4 failed")
+//
+//        pt = try Twofish(key: key).decrypt(data: ct)
+//        XCTAssertEqual(pt.hexa.uppercased(), "39DA69D6BA4997D585B6DC073CA341B2", "Twofish: Decrypt Keysize=192 I=4 failed")
+//    }
+//
+//    func testKey192_5() throws {
+//        let key = Bytes(hex: "39DA69D6BA4997D585B6DC073CA341B288B2B2706B105E36")!
+//        var pt = Bytes(hex: "182B02D81497EA45F9DAACDC29193A65")!
+//        let ct = try Twofish(key: key).encrypt(data: pt)
+//        XCTAssertEqual(ct.hexa.uppercased(), "7AFF7A70CA2FF28AC31DD8AE5DAAAB63", "Twofish: Encrypt Keysize=192 I=5 failed")
+//
+//        pt = try Twofish(key: key).decrypt(data: ct)
+//        XCTAssertEqual(pt.hexa.uppercased(), "182B02D81497EA45F9DAACDC29193A65", "Twofish: Decrypt Keysize=192 I=5 failed")
+//    }
+//
+//    func testKey192_6() throws {
+//        let key = Bytes(hex: "182B02D81497EA45F9DAACDC29193A6539DA69D6BA4997D5")!
+//        var pt = Bytes(hex: "7AFF7A70CA2FF28AC31DD8AE5DAAAB63")!
+//        let ct = try Twofish(key: key).encrypt(data: pt)
+//        XCTAssertEqual(ct.hexa.uppercased(), "D1079B789F666649B6BD7D1629F1F77E", "Twofish: Encrypt Keysize=192 I=6 failed")
+//
+//        pt = try Twofish(key: key).decrypt(data: ct)
+//        XCTAssertEqual(pt.hexa.uppercased(), "7AFF7A70CA2FF28AC31DD8AE5DAAAB63", "Twofish: Decrypt Keysize=192 I=6 failed")
+//    }
+//
+//    func testKey192_7() throws {
+//        let key = Bytes(hex: "7AFF7A70CA2FF28AC31DD8AE5DAAAB63182B02D81497EA45")!
+//        var pt = Bytes(hex: "D1079B789F666649B6BD7D1629F1F77E")!
+//        let ct = try Twofish(key: key).encrypt(data: pt)
+//        XCTAssertEqual(ct.hexa.uppercased(), "3AF6F7CE5BD35EF18BEC6FA787AB506B", "Twofish: Encrypt Keysize=192 I=7 failed")
+//
+//        pt = try Twofish(key: key).decrypt(data: ct)
+//        XCTAssertEqual(pt.hexa.uppercased(), "D1079B789F666649B6BD7D1629F1F77E", "Twofish: Decrypt Keysize=192 I=7 failed")
+//    }
+//
+//    func testKey192_8() throws {
+//        let key = Bytes(hex: "D1079B789F666649B6BD7D1629F1F77E7AFF7A70CA2FF28A")!
+//        var pt = Bytes(hex: "3AF6F7CE5BD35EF18BEC6FA787AB506B")!
+//        let ct = try Twofish(key: key).encrypt(data: pt)
+//        XCTAssertEqual(ct.hexa.uppercased(), "AE8109BFDA85C1F2C5038B34ED691BFF", "Twofish: Encrypt Keysize=192 I=8 failed")
+//
+//        pt = try Twofish(key: key).decrypt(data: ct)
+//        XCTAssertEqual(pt.hexa.uppercased(), "3AF6F7CE5BD35EF18BEC6FA787AB506B", "Twofish: Decrypt Keysize=192 I=8 failed")
+//    }
+//
+//    func testKey192_9() throws {
+//        let key = Bytes(hex: "3AF6F7CE5BD35EF18BEC6FA787AB506BD1079B789F666649")!
+//        var pt = Bytes(hex: "AE8109BFDA85C1F2C5038B34ED691BFF")!
+//        let ct = try Twofish(key: key).encrypt(data: pt)
+//        XCTAssertEqual(ct.hexa.uppercased(), "893FD67B98C550073571BD631263FC78", "Twofish: Encrypt Keysize=192 I=9 failed")
+//
+//        pt = try Twofish(key: key).decrypt(data: ct)
+//        XCTAssertEqual(pt.hexa.uppercased(), "AE8109BFDA85C1F2C5038B34ED691BFF", "Twofish: Decrypt Keysize=192 I=9 failed")
+//    }
+//
+//    func testKey192_10() throws {
+//        let key = Bytes(hex: "AE8109BFDA85C1F2C5038B34ED691BFF3AF6F7CE5BD35EF1")!
+//        var pt = Bytes(hex: "893FD67B98C550073571BD631263FC78")!
+//        let ct = try Twofish(key: key).encrypt(data: pt)
+//        XCTAssertEqual(ct.hexa.uppercased(), "16434FC9C8841A63D58700B5578E8F67", "Twofish: Encrypt Keysize=192 I=10 failed")
+//
+//        pt = try Twofish(key: key).decrypt(data: ct)
+//        XCTAssertEqual(pt.hexa.uppercased(), "893FD67B98C550073571BD631263FC78", "Twofish: Decrypt Keysize=192 I=10 failed")
+//    }
+//
+//    func testKey256_1() throws {
+//        let key = Bytes(hex: "0000000000000000000000000000000000000000000000000000000000000000")!
+//        var pt = Bytes(hex: "00000000000000000000000000000000")!
+//        let ct = try Twofish(key: key).encrypt(data: pt)
+//        XCTAssertEqual(ct.hexa.uppercased(), "57FF739D4DC92C1BD7FC01700CC8216F", "Twofish: Encrypt Keysize=256 I=1 failed")
+//
+//        pt = try Twofish(key: key).decrypt(data: ct)
+//        XCTAssertEqual(pt.hexa.uppercased(), "00000000000000000000000000000000", "Twofish: Decrypt Keysize=256 I=1 failed")
+//    }
+//
+//    func testKey256_2() throws {
+//        let key = Bytes(hex: "0000000000000000000000000000000000000000000000000000000000000000")!
+//        var pt = Bytes(hex: "57FF739D4DC92C1BD7FC01700CC8216F")!
+//        let ct = try Twofish(key: key).encrypt(data: pt)
+//        XCTAssertEqual(ct.hexa.uppercased(), "D43BB7556EA32E46F2A282B7D45B4E0D", "Twofish: Encrypt Keysize=256 I=2 failed")
+//
+//        pt = try Twofish(key: key).decrypt(data: ct)
+//        XCTAssertEqual(pt.hexa.uppercased(), "57FF739D4DC92C1BD7FC01700CC8216F", "Twofish: Decrypt Keysize=256 I=2 failed")
+//    }
+//
+//    func testKey256_3() throws {
+//        let key = Bytes(hex: "57FF739D4DC92C1BD7FC01700CC8216F00000000000000000000000000000000")!
+//        var pt = Bytes(hex: "D43BB7556EA32E46F2A282B7D45B4E0D")!
+//        let ct = try Twofish(key: key).encrypt(data: pt)
+//        XCTAssertEqual(ct.hexa.uppercased(), "90AFE91BB288544F2C32DC239B2635E6", "Twofish: Encrypt Keysize=256 I=3 failed")
+//
+//        pt = try Twofish(key: key).decrypt(data: ct)
+//        XCTAssertEqual(pt.hexa.uppercased(), "D43BB7556EA32E46F2A282B7D45B4E0D", "Twofish: Decrypt Keysize=256 I=3 failed")
+//    }
+//
+//    func testKey256_4() throws {
+//        let key = Bytes(hex: "D43BB7556EA32E46F2A282B7D45B4E0D57FF739D4DC92C1BD7FC01700CC8216F")!
+//        var pt = Bytes(hex: "90AFE91BB288544F2C32DC239B2635E6")!
+//        let ct = try Twofish(key: key).encrypt(data: pt)
+//        XCTAssertEqual(ct.hexa.uppercased(), "6CB4561C40BF0A9705931CB6D408E7FA", "Twofish: Encrypt Keysize=256 I=4 failed")
+//
+//        pt = try Twofish(key: key).decrypt(data: ct)
+//        XCTAssertEqual(pt.hexa.uppercased(), "90AFE91BB288544F2C32DC239B2635E6", "Twofish: Decrypt Keysize=256 I=4 failed")
+//    }
+//
+//    func testKey256_5() throws {
+//        let key = Bytes(hex: "90AFE91BB288544F2C32DC239B2635E6D43BB7556EA32E46F2A282B7D45B4E0D")!
+//        var pt = Bytes(hex: "6CB4561C40BF0A9705931CB6D408E7FA")!
+//        let ct = try Twofish(key: key).encrypt(data: pt)
+//        XCTAssertEqual(ct.hexa.uppercased(), "3059D6D61753B958D92F4781C8640E58", "Twofish: Encrypt Keysize=256 I=5 failed")
+//
+//        pt = try Twofish(key: key).decrypt(data: ct)
+//        XCTAssertEqual(pt.hexa.uppercased(), "6CB4561C40BF0A9705931CB6D408E7FA", "Twofish: Decrypt Keysize=256 I=5 failed")
+//    }
+//
+//    func testKey256_6() throws {
+//        let key = Bytes(hex: "6CB4561C40BF0A9705931CB6D408E7FA90AFE91BB288544F2C32DC239B2635E6")!
+//        var pt = Bytes(hex: "3059D6D61753B958D92F4781C8640E58")!
+//        let ct = try Twofish(key: key).encrypt(data: pt)
+//        XCTAssertEqual(ct.hexa.uppercased(), "E69465770505D7F80EF68CA38AB3A3D6", "Twofish: Encrypt Keysize=256 I=6 failed")
+//
+//        pt = try Twofish(key: key).decrypt(data: ct)
+//        XCTAssertEqual(pt.hexa.uppercased(), "3059D6D61753B958D92F4781C8640E58", "Twofish: Decrypt Keysize=256 I=6 failed")
+//    }
+//
+//    func testKey256_7() throws {
+//        let key = Bytes(hex: "3059D6D61753B958D92F4781C8640E586CB4561C40BF0A9705931CB6D408E7FA")!
+//        var pt = Bytes(hex: "E69465770505D7F80EF68CA38AB3A3D6")!
+//        let ct = try Twofish(key: key).encrypt(data: pt)
+//        XCTAssertEqual(ct.hexa.uppercased(), "5AB67A5F8539A4A5FD9F0373BA463466", "Twofish: Encrypt Keysize=256 I=7 failed")
+//
+//        pt = try Twofish(key: key).decrypt(data: ct)
+//        XCTAssertEqual(pt.hexa.uppercased(), "E69465770505D7F80EF68CA38AB3A3D6", "Twofish: Decrypt Keysize=256 I=7 failed")
+//    }
+//
+//    func testKey256_8() throws {
+//        let key = Bytes(hex: "E69465770505D7F80EF68CA38AB3A3D63059D6D61753B958D92F4781C8640E58")!
+//        var pt = Bytes(hex: "5AB67A5F8539A4A5FD9F0373BA463466")!
+//        let ct = try Twofish(key: key).encrypt(data: pt)
+//        XCTAssertEqual(ct.hexa.uppercased(), "DC096BCD99FC72F79936D4C748E75AF7", "Twofish: Encrypt Keysize=256 I=8 failed")
+//
+//        pt = try Twofish(key: key).decrypt(data: ct)
+//        XCTAssertEqual(pt.hexa.uppercased(), "5AB67A5F8539A4A5FD9F0373BA463466", "Twofish: Decrypt Keysize=256 I=8 failed")
+//    }
+//
+//    func testKey256_9() throws {
+//        let key = Bytes(hex: "5AB67A5F8539A4A5FD9F0373BA463466E69465770505D7F80EF68CA38AB3A3D6")!
+//        var pt = Bytes(hex: "DC096BCD99FC72F79936D4C748E75AF7")!
+//        let ct = try Twofish(key: key).encrypt(data: pt)
+//        XCTAssertEqual(ct.hexa.uppercased(), "C5A3E7CEE0F1B7260528A68FB4EA05F2", "Twofish: Encrypt Keysize=256 I=9 failed")
+//
+//        pt = try Twofish(key: key).decrypt(data: ct)
+//        XCTAssertEqual(pt.hexa.uppercased(), "DC096BCD99FC72F79936D4C748E75AF7", "Twofish: Decrypt Keysize=256 I=9 failed")
+//    }
+//
+//    func testKey256_10() throws {
+//        let key = Bytes(hex: "DC096BCD99FC72F79936D4C748E75AF75AB67A5F8539A4A5FD9F0373BA463466")!
+//        var pt = Bytes(hex: "C5A3E7CEE0F1B7260528A68FB4EA05F2")!
+//        let ct = try Twofish(key: key).encrypt(data: pt)
+//        XCTAssertEqual(ct.hexa.uppercased(), "43D5CEC327B24AB90AD34A79D0469151", "Twofish: Encrypt Keysize=256 I=10 failed")
+//
+//        pt = try Twofish(key: key).decrypt(data: ct)
+//        XCTAssertEqual(pt.hexa.uppercased(), "C5A3E7CEE0F1B7260528A68FB4EA05F2", "Twofish: Decrypt Keysize=256 I=10 failed")
+//    }
+
+    // static var allTests = [
+    //     ("testSalsa20", testSalsa20),
+    // ]
+
+}

+ 2 - 1
Tests/CryptoTests/XCTestManifests.swift

@@ -3,7 +3,8 @@ import XCTest
 #if !canImport(ObjectiveC)
 public func allTests() -> [XCTestCaseEntry] {
     return [
-        testCase(CryptoTests.allTests),
+        testCase(Chacha20Tests.allTests),
+        testCase(Salsa20Tests.allTests),
     ]
 }
 #endif

+ 2 - 4
Tests/KeePassTests/KeePassTests.swift

@@ -2,11 +2,9 @@ import XCTest
 @testable import KeePass
 
 final class KeePassTests: XCTestCase {
+
     func testExample() {
-        // This is an example of a functional test case.
-        // Use XCTAssert and related functions to verify your tests produce the correct
-        // results.
-        XCTAssertEqual(KeePass().text, "Hello, World!")
+
     }
 
     static var allTests = [