mirror of
https://github.com/KeyKeeperApp/KeyKeeper.git
synced 2026-04-26 01:46:40 +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;
|
||||
chunkPosition += 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;
|
||||
}
|
||||
|
||||
@@ -30,5 +30,5 @@ static class FileFormatConstants
|
||||
public const byte GROUP_TYPE_FAVOURITES = 0x02;
|
||||
public const byte GROUP_TYPE_SIMPLE = 0x03;
|
||||
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.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using KeyKeeper.PasswordStore.Crypto;
|
||||
using KeyKeeper.PasswordStore.Crypto.KeyDerivation;
|
||||
@@ -36,11 +37,13 @@ public class PassStoreFileAccessor : IPassStore
|
||||
|
||||
public bool Locked
|
||||
{
|
||||
get { return key != null; }
|
||||
get { return key == null; }
|
||||
}
|
||||
|
||||
public IPassStoreDirectory GetRootDirectory()
|
||||
{
|
||||
if (Locked)
|
||||
throw new InvalidOperationException();
|
||||
return root!;
|
||||
}
|
||||
|
||||
@@ -52,6 +55,10 @@ public class PassStoreFileAccessor : IPassStore
|
||||
public void Unlock(CompositeKey key)
|
||||
{
|
||||
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()
|
||||
@@ -74,7 +81,7 @@ public class PassStoreFileAccessor : IPassStore
|
||||
{
|
||||
throw PassStoreFileException.UnexpectedEndOfFile;
|
||||
}
|
||||
if (magic != FORMAT_MAGIC)
|
||||
if (!magic.SequenceEqual(FORMAT_MAGIC))
|
||||
{
|
||||
throw PassStoreFileException.IncorrectMagicNumber;
|
||||
}
|
||||
@@ -125,9 +132,9 @@ public class PassStoreFileAccessor : IPassStore
|
||||
file.Write(randomPadding);
|
||||
}
|
||||
|
||||
byte[] masterKey = newHeader.KdfInfo.GetKdf().Derive(options.Key, 32);
|
||||
key = newHeader.KdfInfo.GetKdf().Derive(options.Key, 32);
|
||||
// пока предполагаем что везде используется AES
|
||||
OuterEncryptionWriter cryptoWriter = new(file, masterKey, ((OuterAesHeader)newHeader.OuterCryptoHeader).InitVector);
|
||||
OuterEncryptionWriter cryptoWriter = new(file, key, ((OuterAesHeader)newHeader.OuterCryptoHeader).InitVector);
|
||||
|
||||
BinaryWriter wr = new(cryptoWriter);
|
||||
|
||||
@@ -234,6 +241,70 @@ public class PassStoreFileAccessor : IPassStore
|
||||
}
|
||||
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 {}
|
||||
|
||||
@@ -1,13 +1,25 @@
|
||||
using System;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using KeyKeeper.PasswordStore;
|
||||
using KeyKeeper.PasswordStore.Crypto;
|
||||
|
||||
namespace KeyKeeper;
|
||||
|
||||
public partial class RepositoryWindow: Window
|
||||
{
|
||||
public IPassStore? PassStore { private get; init; }
|
||||
|
||||
public RepositoryWindow()
|
||||
{
|
||||
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.Interactivity;
|
||||
using Avalonia.Platform.Storage;
|
||||
using KeyKeeper.PasswordStore;
|
||||
using KeyKeeper.ViewModels;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -39,7 +40,11 @@ namespace KeyKeeper.Views
|
||||
if (file.TryGetLocalPath() is string 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)
|
||||
{
|
||||
(DataContext as MainWindowViewModel)!.OpenVault(path);
|
||||
OpenRepositoryWindow();
|
||||
OpenRepositoryWindow(new PassStoreFileAccessor(path, false, null));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OpenRepositoryWindow()
|
||||
private void OpenRepositoryWindow(IPassStore store)
|
||||
{
|
||||
var repositoryWindow = new RepositoryWindow()
|
||||
{
|
||||
DataContext = this.DataContext,
|
||||
PassStore = store,
|
||||
WindowStartupLocation = WindowStartupLocation.CenterScreen
|
||||
};
|
||||
repositoryWindow.Closed += (s, e) => this.Show();
|
||||
|
||||
Reference in New Issue
Block a user