From 05c76caaaf327cd78f27b5de3b56bf498b684682 Mon Sep 17 00:00:00 2001 From: Slavasil Date: Mon, 24 Nov 2025 19:16:21 +0300 Subject: [PATCH] add OuterEncryptionUtil utility class with a method to quickly check if the outer encryption header of a file is valid --- .../Crypto/OuterEncryptionUtil.cs | 90 +++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 src/KeyKeeper/PasswordStore/Crypto/OuterEncryptionUtil.cs 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); + } + } +}