class Xlsxrb::Ooxml::Crypto::Standard
Implements Microsoft Office Standard Encryption specified in [MS-OFFCRYPTO] Section 2.3.6.
Constants
- CSP_NAME
- ITERATION_COUNT
Public Class Methods
(untyped encryption_info_bytes, untyped encrypted_package_bytes, untyped password) → untyped
Source
# File lib/xlsxrb/ooxml/crypto/standard.rb, line 18 def decrypt(encryption_info_bytes, encrypted_package_bytes, password) salt, encrypted_verifier, verifier_hash_size, encrypted_verifier_hash = parse_encryption_info(encryption_info_bytes) password_str = password.to_s # Derive key K k = derive_key(password_str, salt) # Verify password verifier = aes_ecb_decrypt(k, encrypted_verifier) expected_hash = OpenSSL::Digest::SHA1.digest(verifier) decrypted_hash = aes_ecb_decrypt(k, encrypted_verifier_hash)[0, verifier_hash_size] raise Xlsxrb::InvalidPasswordError, "Standard encryption password verification failed" unless OpenSSL.secure_compare(decrypted_hash, expected_hash) # Decrypt package stream raise Xlsxrb::DecryptionError, "Encrypted package stream is too short" if encrypted_package_bytes.bytesize < 8 total_size = encrypted_package_bytes[0, 8].unpack1("Q<") raise Xlsxrb::DecryptionError, "Encrypted package size is invalid or exceeds limits" if total_size.negative? || total_size > 0x400_000_000 encrypted_data = encrypted_package_bytes[8..] || "".b decrypted = aes_ecb_decrypt(k, encrypted_data) decrypted[0, total_size] || "".b end
Decrypts a Standard-encrypted package.
(untyped plain_bytes, untyped password) → untyped
Source
# File lib/xlsxrb/ooxml/crypto/standard.rb, line 45 def encrypt(plain_bytes, password) password_str = password.to_s salt = SecureRandom.random_bytes(16) verifier = SecureRandom.random_bytes(16) # Derive key K k = derive_key(password_str, salt) # Encrypt verifier encrypted_verifier = aes_ecb_encrypt(k, verifier) # Encrypt verifier hash verifier_hash = OpenSSL::Digest::SHA1.digest(verifier) padded_hash = verifier_hash.ljust(32, "\x00".b) encrypted_verifier_hash = aes_ecb_encrypt(k, padded_hash) # Build EncryptionInfo Stream header_size = 32 + CSP_NAME.bytesize info_stream = +"" # Version & Flags: vMajor=3, vMinor=2, Flags=0x24 (CryptoAPI AES-128) info_stream << [3, 2, 0x24].pack("vvV") # EncryptionHeader info_stream << [header_size].pack("V") info_stream << [0x24, 0, 0x0000660E, 0x00008004, 128, 0x00000018, 0, 0].pack("V8") info_stream << CSP_NAME # EncryptionVerifier info_stream << [16].pack("V") # Salt size info_stream << salt info_stream << encrypted_verifier # 16 bytes info_stream << [20].pack("V") # Verifier hash size (SHA-1 = 20) info_stream << encrypted_verifier_hash # 32 bytes # Build EncryptedPackage Stream padded_plain = plain_bytes padded_plain = padded_plain.ljust(padded_plain.bytesize + 16 - (padded_plain.bytesize % 16), "\x00".b) if (padded_plain.bytesize % 16) != 0 encrypted_pkg_data = aes_ecb_encrypt(k, padded_plain) pkg_stream = [plain_bytes.bytesize].pack("Q<") + encrypted_pkg_data { "EncryptionInfo" => info_stream, "EncryptedPackage" => pkg_stream } end
Encrypts plain zip data into Standard Encryption streams.