diff --git a/src/KeyKeeper/PasswordStore/Crypto/OuterEncryptionReader.cs b/src/KeyKeeper/PasswordStore/Crypto/OuterEncryptionReader.cs index f94e61d..a479e83 100644 --- a/src/KeyKeeper/PasswordStore/Crypto/OuterEncryptionReader.cs +++ b/src/KeyKeeper/PasswordStore/Crypto/OuterEncryptionReader.cs @@ -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; } diff --git a/src/KeyKeeper/PasswordStore/FileFormatConstants.cs b/src/KeyKeeper/PasswordStore/FileFormatConstants.cs index 71bf840..761f5a0 100644 --- a/src/KeyKeeper/PasswordStore/FileFormatConstants.cs +++ b/src/KeyKeeper/PasswordStore/FileFormatConstants.cs @@ -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]; } \ No newline at end of file diff --git a/src/KeyKeeper/PasswordStore/PassStoreFileAccessor.cs b/src/KeyKeeper/PasswordStore/PassStoreFileAccessor.cs index 5a331c2..3876aeb 100644 --- a/src/KeyKeeper/PasswordStore/PassStoreFileAccessor.cs +++ b/src/KeyKeeper/PasswordStore/PassStoreFileAccessor.cs @@ -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 {} diff --git a/src/KeyKeeper/RepositoryWindow.axaml.cs b/src/KeyKeeper/RepositoryWindow.axaml.cs index 56f7f87..5bb3fc9 100644 --- a/src/KeyKeeper/RepositoryWindow.axaml.cs +++ b/src/KeyKeeper/RepositoryWindow.axaml.cs @@ -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)); + } } \ No newline at end of file diff --git a/src/KeyKeeper/Views/MainWindow.axaml.cs b/src/KeyKeeper/Views/MainWindow.axaml.cs index ec9a8a1..b55c457 100644 --- a/src/KeyKeeper/Views/MainWindow.axaml.cs +++ b/src/KeyKeeper/Views/MainWindow.axaml.cs @@ -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();