mirror of
https://github.com/KeyKeeperApp/KeyKeeper.git
synced 2026-05-09 01:46:31 +03:00
fix bugs and add some placeholders
+ fix file magic number check + change marker size 7 -> 8 + set key field when creating-and-unlocking, so Locked becomes false + throw InvalidOperationException in GetRootDirectory when the store is not unlocked + invert Locked property (hahaha) + fix debug logging in OuterEncryptionReader + implement reading the file header from a Stream + PassStoreFileAccessor.Unlock reads and prints out the file header (placeholder for future implementation) + the Create Store button creates an empty file at the selected location + the Open Store button checks the file and calls Unlock
This commit is contained in:
@@ -139,7 +139,7 @@ public class OuterEncryptionReader : Stream
|
|||||||
toRead -= n;
|
toRead -= n;
|
||||||
chunkPosition += n;
|
chunkPosition += n;
|
||||||
position += n;
|
position += n;
|
||||||
Console.WriteLine(string.Format("read={} toread={} pos={}", read, toRead, chunkPosition));
|
Console.WriteLine(string.Format("read={0} toread={1} pos={2}", read, toRead, chunkPosition));
|
||||||
}
|
}
|
||||||
return read;
|
return read;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,5 +30,5 @@ static class FileFormatConstants
|
|||||||
public const byte GROUP_TYPE_FAVOURITES = 0x02;
|
public const byte GROUP_TYPE_FAVOURITES = 0x02;
|
||||||
public const byte GROUP_TYPE_SIMPLE = 0x03;
|
public const byte GROUP_TYPE_SIMPLE = 0x03;
|
||||||
public const byte GROUP_TYPE_CUSTOM = 0xff; // пока не используется
|
public const byte GROUP_TYPE_CUSTOM = 0xff; // пока не используется
|
||||||
public static readonly byte[] BEGIN_MARKER = [0x5f, 0x4f, 0xcf, 0x67, 0xc0, 0x90, 0xd0];
|
public static readonly byte[] BEGIN_MARKER = [0x5f, 0x4f, 0xcf, 0x67, 0xc0, 0x90, 0xd0, 0xe5];
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
using KeyKeeper.PasswordStore.Crypto;
|
using KeyKeeper.PasswordStore.Crypto;
|
||||||
using KeyKeeper.PasswordStore.Crypto.KeyDerivation;
|
using KeyKeeper.PasswordStore.Crypto.KeyDerivation;
|
||||||
@@ -36,11 +37,13 @@ public class PassStoreFileAccessor : IPassStore
|
|||||||
|
|
||||||
public bool Locked
|
public bool Locked
|
||||||
{
|
{
|
||||||
get { return key != null; }
|
get { return key == null; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public IPassStoreDirectory GetRootDirectory()
|
public IPassStoreDirectory GetRootDirectory()
|
||||||
{
|
{
|
||||||
|
if (Locked)
|
||||||
|
throw new InvalidOperationException();
|
||||||
return root!;
|
return root!;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,6 +55,10 @@ public class PassStoreFileAccessor : IPassStore
|
|||||||
public void Unlock(CompositeKey key)
|
public void Unlock(CompositeKey key)
|
||||||
{
|
{
|
||||||
if (!Locked) return;
|
if (!Locked) return;
|
||||||
|
|
||||||
|
using FileStream file = new(filename, FileMode.Open, FileAccess.Read, FileShare.None);
|
||||||
|
FileHeader hdr = FileHeader.ReadFrom(file);
|
||||||
|
Console.WriteLine(hdr); // debug
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Lock()
|
public void Lock()
|
||||||
@@ -74,7 +81,7 @@ public class PassStoreFileAccessor : IPassStore
|
|||||||
{
|
{
|
||||||
throw PassStoreFileException.UnexpectedEndOfFile;
|
throw PassStoreFileException.UnexpectedEndOfFile;
|
||||||
}
|
}
|
||||||
if (magic != FORMAT_MAGIC)
|
if (!magic.SequenceEqual(FORMAT_MAGIC))
|
||||||
{
|
{
|
||||||
throw PassStoreFileException.IncorrectMagicNumber;
|
throw PassStoreFileException.IncorrectMagicNumber;
|
||||||
}
|
}
|
||||||
@@ -125,9 +132,9 @@ public class PassStoreFileAccessor : IPassStore
|
|||||||
file.Write(randomPadding);
|
file.Write(randomPadding);
|
||||||
}
|
}
|
||||||
|
|
||||||
byte[] masterKey = newHeader.KdfInfo.GetKdf().Derive(options.Key, 32);
|
key = newHeader.KdfInfo.GetKdf().Derive(options.Key, 32);
|
||||||
// пока предполагаем что везде используется AES
|
// пока предполагаем что везде используется AES
|
||||||
OuterEncryptionWriter cryptoWriter = new(file, masterKey, ((OuterAesHeader)newHeader.OuterCryptoHeader).InitVector);
|
OuterEncryptionWriter cryptoWriter = new(file, key, ((OuterAesHeader)newHeader.OuterCryptoHeader).InitVector);
|
||||||
|
|
||||||
BinaryWriter wr = new(cryptoWriter);
|
BinaryWriter wr = new(cryptoWriter);
|
||||||
|
|
||||||
@@ -234,6 +241,70 @@ public class PassStoreFileAccessor : IPassStore
|
|||||||
}
|
}
|
||||||
return written;
|
return written;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static FileHeader ReadFrom(Stream s)
|
||||||
|
{
|
||||||
|
BinaryReader rd = new(s);
|
||||||
|
{
|
||||||
|
byte[] magic = new byte[8];
|
||||||
|
if (rd.Read(magic, 0, 8) < 8)
|
||||||
|
throw PassStoreFileException.UnexpectedEndOfFile;
|
||||||
|
if (!magic.SequenceEqual(FORMAT_MAGIC))
|
||||||
|
throw PassStoreFileException.IncorrectMagicNumber;
|
||||||
|
}
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ushort major, minor;
|
||||||
|
major = rd.ReadUInt16();
|
||||||
|
minor = rd.ReadUInt16();
|
||||||
|
if (major != FORMAT_VERSION_MAJOR || minor != FORMAT_VERSION_MINOR)
|
||||||
|
throw PassStoreFileException.UnsupportedVersion;
|
||||||
|
|
||||||
|
byte saltLen = rd.ReadByte();
|
||||||
|
if (saltLen < MIN_MASTER_SALT_LEN || saltLen > MAX_MASTER_SALT_LEN)
|
||||||
|
throw PassStoreFileException.InvalidCryptoHeader;
|
||||||
|
|
||||||
|
byte[] salt = new byte[saltLen];
|
||||||
|
if (rd.Read(salt) < saltLen)
|
||||||
|
throw PassStoreFileException.UnexpectedEndOfFile;
|
||||||
|
|
||||||
|
byte typeDiscrim = rd.ReadByte();
|
||||||
|
OuterEncryptionHeader outerEncrHdr;
|
||||||
|
if (typeDiscrim == ENCRYPT_ALGO_AES)
|
||||||
|
{
|
||||||
|
byte[] iv = new byte[16];
|
||||||
|
if (rd.Read(iv) < 16)
|
||||||
|
throw PassStoreFileException.UnexpectedEndOfFile;
|
||||||
|
outerEncrHdr = new OuterAesHeader(iv);
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
throw PassStoreFileException.InvalidCryptoHeader;
|
||||||
|
}
|
||||||
|
|
||||||
|
typeDiscrim = rd.ReadByte();
|
||||||
|
KdfHeader kdfHdr;
|
||||||
|
if (typeDiscrim == KDF_TYPE_AESKDF)
|
||||||
|
{
|
||||||
|
int rounds = rd.Read7BitEncodedInt();
|
||||||
|
byte[] seed = new byte[32];
|
||||||
|
if (rd.Read(seed) < 32)
|
||||||
|
throw PassStoreFileException.UnexpectedEndOfFile;
|
||||||
|
kdfHdr = new AesKdfHeader(rounds, seed);
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
throw PassStoreFileException.InvalidCryptoHeader;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new FileHeader(
|
||||||
|
major, minor, salt,
|
||||||
|
outerEncrHdr, kdfHdr
|
||||||
|
);
|
||||||
|
}
|
||||||
|
catch (EndOfStreamException)
|
||||||
|
{
|
||||||
|
throw PassStoreFileException.UnexpectedEndOfFile;
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
record OuterEncryptionHeader {}
|
record OuterEncryptionHeader {}
|
||||||
|
|||||||
@@ -1,13 +1,25 @@
|
|||||||
|
using System;
|
||||||
using Avalonia;
|
using Avalonia;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Markup.Xaml;
|
using Avalonia.Markup.Xaml;
|
||||||
|
using KeyKeeper.PasswordStore;
|
||||||
|
using KeyKeeper.PasswordStore.Crypto;
|
||||||
|
|
||||||
namespace KeyKeeper;
|
namespace KeyKeeper;
|
||||||
|
|
||||||
public partial class RepositoryWindow: Window
|
public partial class RepositoryWindow: Window
|
||||||
{
|
{
|
||||||
|
public IPassStore? PassStore { private get; init; }
|
||||||
|
|
||||||
public RepositoryWindow()
|
public RepositoryWindow()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void OnOpened(EventArgs e)
|
||||||
|
{
|
||||||
|
base.OnOpened(e);
|
||||||
|
if (PassStore!.Locked)
|
||||||
|
PassStore.Unlock(new CompositeKey("blablabla", null));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -3,6 +3,7 @@ using Avalonia.Controls;
|
|||||||
using Avalonia.Input;
|
using Avalonia.Input;
|
||||||
using Avalonia.Interactivity;
|
using Avalonia.Interactivity;
|
||||||
using Avalonia.Platform.Storage;
|
using Avalonia.Platform.Storage;
|
||||||
|
using KeyKeeper.PasswordStore;
|
||||||
using KeyKeeper.ViewModels;
|
using KeyKeeper.ViewModels;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@@ -39,7 +40,11 @@ namespace KeyKeeper.Views
|
|||||||
if (file.TryGetLocalPath() is string path)
|
if (file.TryGetLocalPath() is string path)
|
||||||
{
|
{
|
||||||
(DataContext as MainWindowViewModel)!.CreateVault(path);
|
(DataContext as MainWindowViewModel)!.CreateVault(path);
|
||||||
OpenRepositoryWindow();
|
OpenRepositoryWindow(new PassStoreFileAccessor(path, true, new StoreCreationOptions()
|
||||||
|
{
|
||||||
|
Key = new PasswordStore.Crypto.CompositeKey("blablabla", null),
|
||||||
|
LockTimeoutSeconds = 800,
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -70,16 +75,17 @@ namespace KeyKeeper.Views
|
|||||||
if (file.TryGetLocalPath() is string path)
|
if (file.TryGetLocalPath() is string path)
|
||||||
{
|
{
|
||||||
(DataContext as MainWindowViewModel)!.OpenVault(path);
|
(DataContext as MainWindowViewModel)!.OpenVault(path);
|
||||||
OpenRepositoryWindow();
|
OpenRepositoryWindow(new PassStoreFileAccessor(path, false, null));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OpenRepositoryWindow()
|
private void OpenRepositoryWindow(IPassStore store)
|
||||||
{
|
{
|
||||||
var repositoryWindow = new RepositoryWindow()
|
var repositoryWindow = new RepositoryWindow()
|
||||||
{
|
{
|
||||||
DataContext = this.DataContext,
|
DataContext = this.DataContext,
|
||||||
|
PassStore = store,
|
||||||
WindowStartupLocation = WindowStartupLocation.CenterScreen
|
WindowStartupLocation = WindowStartupLocation.CenterScreen
|
||||||
};
|
};
|
||||||
repositoryWindow.Closed += (s, e) => this.Show();
|
repositoryWindow.Closed += (s, e) => this.Show();
|
||||||
|
|||||||
Reference in New Issue
Block a user