diff --git a/src/KeyKeeper/PasswordStore/Crypto/OuterEncryptionUtil.cs b/src/KeyKeeper/PasswordStore/Crypto/OuterEncryptionUtil.cs
new file mode 100644
index 0000000..d10afd5
--- /dev/null
+++ b/src/KeyKeeper/PasswordStore/Crypto/OuterEncryptionUtil.cs
@@ -0,0 +1,90 @@
+using System;
+using System.IO;
+using static KeyKeeper.PasswordStore.FileFormatConstants;
+
+namespace KeyKeeper.PasswordStore.Crypto;
+
+public static class OuterEncryptionUtil
+{
+ ///
+ /// Проверяет корректность заголовка внешнего шифрования,
+ /// который содержит соль для мастер-ключа и параметры шифрования +
+ /// генерации ключа. Сдвигает указатель потока f на первый байт после
+ /// заголовка.
+ ///
+ /// Поток, указатель которого стоит на начале заголовка
+ /// внешнего шифрования.
+ /// Если заголовок содержит некорректные поля или неполный
+ public static void CheckOuterEncryptionHeader(FileStream f)
+ {
+ BinaryReader rd = new(f);
+ byte masterSaltLen;
+ try
+ {
+ masterSaltLen = rd.ReadByte();
+ }
+ catch (EndOfStreamException)
+ {
+ throw PassStoreFileException.UnexpectedEndOfFile;
+ }
+ if (masterSaltLen < MIN_MASTER_SALT_LEN || masterSaltLen > MAX_MASTER_SALT_LEN)
+ {
+ throw PassStoreFileException.InvalidCryptoHeader;
+ }
+
+ f.Seek(masterSaltLen, SeekOrigin.Current);
+
+ byte encryptAlgo;
+ try
+ {
+ encryptAlgo = rd.ReadByte();
+ }
+ catch (EndOfStreamException)
+ {
+ throw PassStoreFileException.UnexpectedEndOfFile;
+ }
+
+ if (encryptAlgo == ENCRYPT_ALGO_AES)
+ {
+ // пропустить 16 байт вектора инициализации AES
+ f.Seek(16, SeekOrigin.Current);
+ }
+ else
+ {
+ throw PassStoreFileException.InvalidCryptoHeader;
+ }
+
+ byte keyDerivationFunctionType;
+ try
+ {
+ keyDerivationFunctionType = rd.ReadByte();
+ }
+ catch (EndOfStreamException)
+ {
+ throw PassStoreFileException.UnexpectedEndOfFile;
+ }
+
+ if (keyDerivationFunctionType == KDF_TYPE_AESKDF)
+ {
+ int nRounds;
+ try
+ {
+ nRounds = rd.Read7BitEncodedInt();
+ }
+ catch (EndOfStreamException)
+ {
+ throw PassStoreFileException.UnexpectedEndOfFile;
+ }
+ catch (FormatException)
+ {
+ throw PassStoreFileException.InvalidCryptoHeader;
+ }
+ if (nRounds < MIN_AESKDF_ROUNDS || nRounds > MAX_AESKDF_ROUNDS)
+ {
+ throw PassStoreFileException.InvalidCryptoHeader;
+ }
+ // пропустить 32 байта сида AES-KDF
+ f.Seek(32, SeekOrigin.Current);
+ }
+ }
+}