Class: Omnizip::Formats::SevenZip::HeaderEncryptor

Inherits:
Object
  • Object
show all
Includes:
Constants
Defined in:
lib/omnizip/formats/seven_zip/header_encryptor.rb

Overview

7z header encryption using AES-256 Encrypts archive headers to hide filenames and structure

Constant Summary collapse

AES_KEY_SIZE =

AES-256 parameters

32
AES_IV_SIZE =

256 bits

16
SALT_SIZE =

128 bits

16
PBKDF2_ITERATIONS =

PBKDF2 parameters

262_144

Constants included from Constants

Constants::MAJOR_VERSION, Constants::MAX_NUM_BONDS, Constants::MAX_NUM_CODERS, Constants::MAX_NUM_PACK_STREAMS, Constants::MINOR_VERSION, Constants::SIGNATURE, Constants::SIGNATURE_SIZE, Constants::START_HEADER_SIZE

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(password) ⇒ HeaderEncryptor

Initialize encryptor with password

Parameters:

  • password (String)

    Encryption password



26
27
28
29
30
# File 'lib/omnizip/formats/seven_zip/header_encryptor.rb', line 26

def initialize(password)
  @password = password
  @salt = nil
  @iv = nil
end

Instance Attribute Details

#ivObject (readonly)

256K iterations for strong key derivation



21
22
23
# File 'lib/omnizip/formats/seven_zip/header_encryptor.rb', line 21

def iv
  @iv
end

#passwordObject (readonly)

256K iterations for strong key derivation



21
22
23
# File 'lib/omnizip/formats/seven_zip/header_encryptor.rb', line 21

def password
  @password
end

#saltObject (readonly)

256K iterations for strong key derivation



21
22
23
# File 'lib/omnizip/formats/seven_zip/header_encryptor.rb', line 21

def salt
  @salt
end

Instance Method Details

#calculate_crc(data) ⇒ Integer

Calculate CRC of header data

Parameters:

  • data (String)

    Header data

Returns:

  • (Integer)

    CRC32 value



124
125
126
127
128
# File 'lib/omnizip/formats/seven_zip/header_encryptor.rb', line 124

def calculate_crc(data)
  crc = Omnizip::Checksums::Crc32.new
  crc.update(data)
  crc.value
end

#decrypt(encrypted_data, salt, iv) ⇒ String

Decrypt header data

Parameters:

  • encrypted_data (String)

    Encrypted bytes

  • salt (String)

    Salt used during encryption

  • iv (String)

    Initialization vector

Returns:

  • (String)

    Decrypted header bytes

Raises:

  • (RuntimeError)

    if password is incorrect



71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/omnizip/formats/seven_zip/header_encryptor.rb', line 71

def decrypt(encrypted_data, salt, iv)
  # Derive decryption key from password
  key = derive_key(@password, salt)

  # Decrypt data
  decipher = OpenSSL::Cipher.new("AES-256-CBC")
  decipher.decrypt
  decipher.key = key
  decipher.iv = iv
  # Ensure padding validation is enabled (default, but explicit)
  decipher.padding = 1

  begin
    decipher.update(encrypted_data) + decipher.final
  rescue OpenSSL::Cipher::CipherError => e
    raise "Failed to decrypt header: incorrect password or corrupted data (#{e.message})"
  rescue StandardError => e
    raise "Failed to decrypt header: incorrect password or corrupted data (#{e.message})"
  end
end

#derive_key(password, salt) ⇒ String

Derive encryption key from password using PBKDF2

Parameters:

  • password (String)

    User password

  • salt (String)

    Random salt

Returns:

  • (String)

    Derived key



97
98
99
100
101
102
103
104
105
# File 'lib/omnizip/formats/seven_zip/header_encryptor.rb', line 97

def derive_key(password, salt)
  OpenSSL::PKCS5.pbkdf2_hmac(
    password,
    salt,
    PBKDF2_ITERATIONS,
    AES_KEY_SIZE,
    OpenSSL::Digest.new("SHA256"),
  )
end

#encrypt(header_data) ⇒ Hash

Encrypt header data

Parameters:

  • header_data (String)

    Unencrypted header bytes

Returns:

  • (Hash)

    Encrypted data with metadata

    • :data [String] Encrypted bytes
    • :salt [String] Salt used for key derivation
    • :iv [String] Initialization vector
    • :size [Integer] Original size before encryption


40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# File 'lib/omnizip/formats/seven_zip/header_encryptor.rb', line 40

def encrypt(header_data)
  # Generate random salt and IV
  @salt = OpenSSL::Random.random_bytes(SALT_SIZE)
  @iv = OpenSSL::Random.random_bytes(AES_IV_SIZE)

  # Derive encryption key from password
  key = derive_key(@password, @salt)

  # Encrypt data
  cipher = OpenSSL::Cipher.new("AES-256-CBC")
  cipher.encrypt
  cipher.key = key
  cipher.iv = @iv

  encrypted = cipher.update(header_data) + cipher.final

  {
    data: encrypted,
    salt: @salt,
    iv: @iv,
    size: header_data.bytesize,
  }
end

#verify_password(encrypted_data, salt, iv) ⇒ Boolean

Verify password against encrypted header

Parameters:

  • encrypted_data (String)

    Encrypted header

  • salt (String)

    Salt used

  • iv (String)

    IV used

Returns:

  • (Boolean)

    true if password can decrypt



113
114
115
116
117
118
# File 'lib/omnizip/formats/seven_zip/header_encryptor.rb', line 113

def verify_password(encrypted_data, salt, iv)
  decrypt(encrypted_data, salt, iv)
  true
rescue StandardError
  false
end