/// <summary> /// Ensures the writer's serialization settings are correct for the given user preferences. /// </summary> /// <param name="cipher">The algorithm to use for encrypting the database.</param> /// <param name="rngAlgorithm">The random number generator used for String protection.</param> /// <param name="compression">The document compression algorithm.</param> /// <param name="kdfParams">Recipe for transforming the raw key. This will be reseeded.</param> private void SeedHeaderData( EncryptionAlgorithm cipher, RngAlgorithm rngAlgorithm, CompressionAlgorithm compression, KdfParameters kdfParams ) { if (kdfParams == null) { throw new ArgumentNullException(nameof(kdfParams)); } KdbxVersion version = KdbxVersion.Three; if (cipher == EncryptionAlgorithm.ChaCha20 || rngAlgorithm == RngAlgorithm.ChaCha20 || !kdfParams.Uuid.Equals(AesParameters.AesUuid)) { DebugHelper.Trace("Using KDBX4 for serialization due to header parameters"); version = KdbxVersion.Four; } this.parameters = new KdbxSerializationParameters(version) { Compression = compression }; // "Stream start bytes" are random data encrypted at the beginning // of the KDBX data block. They have been superceded by HMAC authentication. IBuffer streamStartBytes; if (this.parameters.UseHmacBlocks) { streamStartBytes = new byte[0].AsBuffer(); } else { streamStartBytes = CryptographicBuffer.GenerateRandom(32); } HeaderData = new KdbxHeaderData(KdbxHeaderData.Mode.Write) { Cipher = cipher, // This will automatically set EncryptionIV Compression = compression, MasterSeed = CryptographicBuffer.GenerateRandom(32), KdfParameters = kdfParams.Reseed(), StreamStartBytes = streamStartBytes, InnerRandomStreamKey = CryptographicBuffer.GenerateRandom(32).ToArray(), InnerRandomStream = rngAlgorithm }; }
/// <summary> /// Validates that the document has a valid, supported KeePass version. /// </summary> /// <param name="reader">A DataReader over the document file.</param> /// <returns>A Task representing the result of the validation.</returns> private async Task <ReaderResult> ValidateVersion(DataReader reader) { await reader.LoadAsync(4); uint version = reader.ReadUInt32(); uint maskedVersion = version & FileVersionMask; uint maskedLegacyFormat = FileVersion32_3 & FileVersionMask; uint maskedModernFormat = FileVersion32_4 & FileVersionMask; if (maskedVersion <= maskedLegacyFormat) { this.parameters = new KdbxSerializationParameters(KdbxVersion.Three); } else if (maskedVersion == maskedModernFormat) { this.parameters = new KdbxSerializationParameters(KdbxVersion.Four); } else { DebugHelper.Assert(maskedVersion > maskedModernFormat); return(new ReaderResult(KdbxParserCode.Version)); } // Based on the version, initialize a map of required headers foreach (OuterHeaderField value in Enum.GetValues(typeof(OuterHeaderField)) .Cast <OuterHeaderField>()) { MemberInfo enumMember = typeof(OuterHeaderField).GetMember(value.ToString()) .FirstOrDefault(); // Skip optional headers regardless of version if (enumMember.GetCustomAttribute <OptionalAttribute>() != null) { continue; } // Get the headers that support this version KdbxVersionSupportAttribute versionAttr = enumMember.GetCustomAttribute <KdbxVersionSupportAttribute>(); if (versionAttr == null || versionAttr.Supports(this.parameters.Version)) { this.headerInitializationMap[value] = false; } } return(ReaderResult.Success); }