Class: Axlsx::MsOffCrypto
- Inherits:
-
Object
- Object
- Axlsx::MsOffCrypto
- Defined in:
- lib/axlsx/util/ms_off_crypto.rb
Overview
The MsOffCrypto class implements ECMA-367 encryption based on the MS-OFF-CRYPTO specification
Instance Attribute Summary collapse
-
#file_name ⇒ String
retruns the file name of the archive to be encrypted.
-
#password ⇒ String
returns the raw password used in encryption.
Instance Method Summary collapse
-
#create_encryption_info ⇒ String
Generates an encryption info structure.
-
#create_key ⇒ Object
2.3.4.7 ECMA-376 Document Encryption Key Generation (Standard Encryption).
-
#create_verifier_hash ⇒ Object
2.3.3.
-
#decrypt(data) ⇒ String
dencrypts the data proved.
-
#encrypt(data) ⇒ String
encrypts the data proved.
-
#encrypt_package(file_name) ⇒ Object
size of unencrypted package? concated with encrypted package.
-
#encrypted_package ⇒ String
encrypts and returns the package specified by the file name.
-
#encrypted_verifier ⇒ String
returns the verifier encrytped.
-
#encrypted_verifier_hash ⇒ String
returns the verifier hash encrypted.
-
#encryption_info ⇒ String
returns the encryption info for this instance of ms-off-crypto.
-
#initialize(file_name, pwd) ⇒ MsOffCrypto
constructor
Creates a new MsOffCrypto Object.
-
#key ⇒ String
returns an encryption key.
-
#salt ⇒ String
returns a random salt.
-
#save ⇒ Object
Generates a new CBF file based on this instance of ms-off-crypto and overwrites the unencrypted file.
-
#verifier ⇒ String
returns a random verifier.
-
#verifier_hash ⇒ String
returns a verifier hash.
-
#verify_password ⇒ Boolean
ensures that the a hashed decryption of the encryption verifier matches the decrypted verifier hash.
Constructor Details
#initialize(file_name, pwd) ⇒ MsOffCrypto
Creates a new MsOffCrypto Object
14 15 16 17 |
# File 'lib/axlsx/util/ms_off_crypto.rb', line 14 def initialize(file_name, pwd) self.password = pwd self.file_name = file_name end |
Instance Attribute Details
#file_name ⇒ String
retruns the file name of the archive to be encrypted
38 39 40 |
# File 'lib/axlsx/util/ms_off_crypto.rb', line 38 def file_name @file_name end |
#password ⇒ String
returns the raw password used in encryption
27 28 29 |
# File 'lib/axlsx/util/ms_off_crypto.rb', line 27 def password @password end |
Instance Method Details
#create_encryption_info ⇒ String
Generates an encryption info structure
105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 |
# File 'lib/axlsx/util/ms_off_crypto.rb', line 105 def create_encryption_info header = [3, 0, 2, 0] # version # Header flags copy header.concat [0x24, 0, 0, 0] #flags -- VERY UNSURE ABOUT THIS STILL # header.concat [0, 0, 0, 0] #unused header.concat [0xA4, 0, 0, 0] #length # Header header.concat [0x24, 0, 0, 0] #flags again # header.concat [0, 0, 0, 0] #unused again, header.concat [0x0E, 0x66, 0, 0] #alg id header.concat [0x04, 0x80, 0, 0] #alg hash id header.concat [key.size, 0, 0, 0] #key size header.concat [0x18, 0, 0, 0] #provider type # header.concat [0, 0, 0, 0] #reserved 1 # header.concat [0, 0, 0, 0] #reserved 2 header.concat [0xA0, 0xC7, 0xDC, 0x2, 0, 0, 0, 0] header.concat "Microsoft Enhanced RSA and AES Cryptographic Provider (Prototype)".bytes.to_a.pack('s*').bytes.to_a header.concat [0, 0] #null terminator #Salt Size header.concat [salt.bytes.to_a.size].pack('l').bytes.to_a #Salt header.concat salt.bytes.to_a.pack('c*').bytes.to_a # encryption verifier header.concat encrypted_verifier.bytes.to_a.pack('c*').bytes.to_a # verifier hash size -- MUST BE 32 bytes header.concat [verifier_hash.bytes.to_a.size].pack('l').bytes.to_a #encryption verifier hash header.concat encrypted_verifier_hash.bytes.to_a.pack('c*').bytes.to_a header.flatten! header.pack('c*') end |
#create_key ⇒ Object
2.3.4.7 ECMA-376 Document Encryption Key Generation (Standard Encryption)
148 149 150 151 152 153 154 155 156 157 158 |
# File 'lib/axlsx/util/ms_off_crypto.rb', line 148 def create_key sha = Digest::SHA1.new() << (salt + @password) (0..49999).each { |i| sha.update(i.to_s+sha.to_s) } key = sha.update(sha.to_s+'0').digest a = key.bytes.each_with_index.map { |item, i| 0x36 ^ item } x1 = Digest::SHA1.digest((a.concat Array.new(64 - key.size, 0x36)).to_s) a = key.bytes.each_with_index.map { |item, i| 0x5C ^ item } x2 = Digest::SHA1.digest( (a.concat Array.new(64 - key.size, 0x5C) ).to_s) x3 = x1 + x2 x3.bytes.to_a[(0..31)].pack('c*') end |
#create_verifier_hash ⇒ Object
2.3.3
142 143 144 145 |
# File 'lib/axlsx/util/ms_off_crypto.rb', line 142 def create_verifier_hash vh = Digest::SHA1.digest(verifier) vh << Array.new(32 - vh.size, 0).join('') end |
#decrypt(data) ⇒ String
dencrypts the data proved
181 182 183 184 185 186 |
# File 'lib/axlsx/util/ms_off_crypto.rb', line 181 def decrypt(data) aes = OpenSSL::Cipher.new("AES-128-ECB") aes.decrypt aes.key = key aes.update(data) << aes.final end |
#encrypt(data) ⇒ String
encrypts the data proved
171 172 173 174 175 176 |
# File 'lib/axlsx/util/ms_off_crypto.rb', line 171 def encrypt(data) aes = OpenSSL::Cipher.new("AES-128-ECB") aes.encrypt aes.key = key aes.update(data) << aes.final end |
#encrypt_package(file_name) ⇒ Object
size of unencrypted package? concated with encrypted package
97 98 99 100 101 |
# File 'lib/axlsx/util/ms_off_crypto.rb', line 97 def encrypt_package(file_name) package = File.open(file_name, 'r') crypt_pack = encrypt(package.read) [crypt_pack.size].pack('q') + crypt_pack end |
#encrypted_package ⇒ String
encrypts and returns the package specified by the file name
50 51 52 |
# File 'lib/axlsx/util/ms_off_crypto.rb', line 50 def encrypted_package @encrypted_package ||= encrypt_package(file_name) end |
#encrypted_verifier ⇒ String
returns the verifier encrytped
74 75 76 |
# File 'lib/axlsx/util/ms_off_crypto.rb', line 74 def encrypted_verifier @encrypted_verifier ||= encrypt(verifier) end |
#encrypted_verifier_hash ⇒ String
returns the verifier hash encrypted
80 81 82 |
# File 'lib/axlsx/util/ms_off_crypto.rb', line 80 def encrypted_verifier_hash @encrypted_verifier_hash ||= encrypt(verifier_hash) end |
#encryption_info ⇒ String
returns the encryption info for this instance of ms-off-crypto
56 57 58 |
# File 'lib/axlsx/util/ms_off_crypto.rb', line 56 def encryption_info @encryption_info ||= create_encryption_info end |
#key ⇒ String
returns an encryption key
92 93 94 |
# File 'lib/axlsx/util/ms_off_crypto.rb', line 92 def key @key ||= create_key end |
#salt ⇒ String
returns a random salt
62 63 64 |
# File 'lib/axlsx/util/ms_off_crypto.rb', line 62 def salt @salt ||= Digest::SHA1.digest(rand(16**16).to_s) end |
#save ⇒ Object
Generates a new CBF file based on this instance of ms-off-crypto and overwrites the unencrypted file.
20 21 22 23 |
# File 'lib/axlsx/util/ms_off_crypto.rb', line 20 def save cfb = Cbf.new(self) cfb.save end |
#verifier ⇒ String
returns a random verifier
68 69 70 |
# File 'lib/axlsx/util/ms_off_crypto.rb', line 68 def verifier @verifier ||= rand(16**16).to_s end |
#verifier_hash ⇒ String
returns a verifier hash
86 87 88 |
# File 'lib/axlsx/util/ms_off_crypto.rb', line 86 def verifier_hash @verifier_hash ||= create_verifier_hash end |
#verify_password ⇒ Boolean
ensures that the a hashed decryption of the encryption verifier matches the decrypted verifier hash.
162 163 164 165 166 |
# File 'lib/axlsx/util/ms_off_crypto.rb', line 162 def verify_password v = Digest::SHA1.digest decrypt(@encrypted_verifier) vh = decrypt(@encrypted_verifier_hash) vh[0..15] == v[0..15] end |