< Summary

Information
Class: System.IO.Compression.WinZipAesKeyMaterial
Assembly: System.IO.Compression
File(s): D:\runner\runtime\src\libraries\System.IO.Compression\src\System\IO\Compression\WinZipAesKeyMaterial.cs
Line coverage
92%
Covered lines: 52
Uncovered lines: 4
Coverable lines: 56
Total lines: 99
Line coverage: 92.8%
Branch coverage
62%
Covered branches: 5
Total branches: 8
Branch coverage: 62.5%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
.ctor(...)50%2281.81%
Create(...)66.66%6694.87%
GetSaltSize(...)100%11100%

File(s)

D:\runner\runtime\src\libraries\System.IO.Compression\src\System\IO\Compression\WinZipAesKeyMaterial.cs

#LineLine coverage
 1// Licensed to the .NET Foundation under one or more agreements.
 2// The .NET Foundation licenses this file to you under the MIT license.
 3
 4using System.Buffers;
 5using System.Diagnostics;
 6using System.Runtime.Versioning;
 7using System.Security.Cryptography;
 8using System.Text;
 9
 10namespace System.IO.Compression
 11{
 12    /// <summary>
 13    /// Represents the parsed components of WinZip AES key material.
 14    /// The key material layout is: [salt][encryption key][HMAC key][password verifier (2 bytes)].
 15    /// </summary>
 16    internal readonly struct WinZipAesKeyMaterial
 17    {
 38418        public byte[] Salt { get; }
 19219        public byte[] EncryptionKey { get; }
 19220        public byte[] HmacKey { get; }
 38421        public byte[] PasswordVerifier { get; }
 22        public int KeySizeBits { get; }
 28823        public int SaltSize { get; }
 24
 25        private WinZipAesKeyMaterial(byte[] salt, byte[] encryptionKey, byte[] hmacKey, byte[] passwordVerifier, int key
 28826        {
 28827            if (OperatingSystem.IsBrowser())
 028            {
 029                throw new PlatformNotSupportedException(SR.WinZipEncryptionNotSupportedOnPlatform);
 30            }
 28831            Salt = salt;
 28832            EncryptionKey = encryptionKey;
 28833            HmacKey = hmacKey;
 28834            PasswordVerifier = passwordVerifier;
 28835            KeySizeBits = keySizeBits;
 28836            SaltSize = GetSaltSize(keySizeBits);
 28837        }
 38
 39        /// <summary>
 40        /// Derives key material from a password and optional salt using PBKDF2-SHA1.
 41        /// </summary>
 42        internal static unsafe WinZipAesKeyMaterial Create(ReadOnlySpan<char> password, byte[]? salt, int keySizeBits)
 28843        {
 28844            if (OperatingSystem.IsBrowser())
 045            {
 046                throw new PlatformNotSupportedException(SR.WinZipEncryptionNotSupportedOnPlatform);
 47            }
 28848            int saltSize = GetSaltSize(keySizeBits);
 28849            int keySizeBytes = keySizeBits / 8;
 28850            int totalKeySize = checked(keySizeBytes + keySizeBytes + 2);
 51
 52            byte[] saltBytes;
 28853            if (salt is null)
 9654            {
 9655                saltBytes = new byte[saltSize];
 9656                RandomNumberGenerator.Fill(saltBytes);
 9657            }
 58            else
 19259            {
 19260                Debug.Assert(salt.Length == saltSize, $"Salt must be {saltSize} bytes for AES-{keySizeBits}.");
 19261                saltBytes = salt;
 19262            }
 63
 28864            int maxPasswordByteCount = Encoding.UTF8.GetMaxByteCount(password.Length);
 28865            byte[] rentedPasswordBytes = ArrayPool<byte>.Shared.Rent(maxPasswordByteCount);
 28866            Debug.Assert(totalKeySize <= 66, "totalKeySize should be at most 66 bytes (AES-256: 32 + 32 + 2)");
 28867            Span<byte> derivedKey = stackalloc byte[totalKeySize];
 68
 69            try
 28870            {
 28871                int actualByteCount = Encoding.UTF8.GetBytes(password, rentedPasswordBytes);
 28872                Span<byte> passwordSpan = rentedPasswordBytes.AsSpan(0, actualByteCount);
 73
 28874                Rfc2898DeriveBytes.Pbkdf2(
 28875                    passwordSpan,
 28876                    saltBytes,
 28877                    derivedKey,
 28878                    1000, // iteration count specified by the WinZip AE-1/AE-2 specification
 28879                    HashAlgorithmName.SHA1);
 80
 81                // Slice the derived key directly into its components instead of
 82                // round-tripping through a combined array and Parse.
 28883                byte[] encryptionKey = derivedKey.Slice(0, keySizeBytes).ToArray();
 28884                byte[] hmacKey = derivedKey.Slice(keySizeBytes, keySizeBytes).ToArray();
 28885                byte[] passwordVerifier = derivedKey.Slice(keySizeBytes + keySizeBytes, 2).ToArray();
 86
 28887                return new WinZipAesKeyMaterial(saltBytes, encryptionKey, hmacKey, passwordVerifier, keySizeBits);
 88            }
 89            finally
 28890            {
 28891                CryptographicOperations.ZeroMemory(rentedPasswordBytes);
 28892                CryptographicOperations.ZeroMemory(derivedKey);
 28893                ArrayPool<byte>.Shared.Return(rentedPasswordBytes);
 28894            }
 28895        }
 96
 67297        internal static int GetSaltSize(int keySizeBits) => keySizeBits / 16;
 98    }
 99}