Class: Xlsxrb::Ooxml::Crypto::Standard
- Inherits:
-
Object
- Object
- Xlsxrb::Ooxml::Crypto::Standard
- Defined in:
- lib/xlsxrb/ooxml/crypto/standard.rb,
sig/generated/xlsxrb/ooxml/crypto/standard.rbs
Overview
Implements Microsoft Office Standard Encryption specified in [MS-OFFCRYPTO] Section 2.3.6.
Constant Summary collapse
- CSP_NAME =
"Microsoft Enhanced RSA and AES Cryptographic Provider\x00".encode("UTF-16LE").b.freeze
- ITERATION_COUNT =
50_000
Class Method Summary collapse
-
.decrypt(encryption_info_bytes, encrypted_package_bytes, password) ⇒ Object
Decrypts a Standard-encrypted package.
-
.encrypt(plain_bytes, password) ⇒ Object
Encrypts plain zip data into Standard Encryption streams.
Class Method Details
.decrypt(encryption_info_bytes, encrypted_package_bytes, password) ⇒ Object
Decrypts a Standard-encrypted package.
18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
# 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 |
.encrypt(plain_bytes, password) ⇒ Object
Encrypts plain zip data into Standard Encryption streams.
45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 |
# 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 |