diff options
| author | Randy Morgan <[email protected]> | 2012-06-20 08:21:06 +0900 |
|---|---|---|
| committer | Randy Morgan <[email protected]> | 2012-06-20 08:21:06 +0900 |
| commit | 53235912acc99ef8801e43edf3fccfa2dced3d3e (patch) | |
| tree | 5534d319259a40b1404f19a2b1c8de8252a329b0 /lib | |
| parent | 16a7fd2450581353efc9ec0daf267b28ea87fe02 (diff) | |
| download | caxlsx-53235912acc99ef8801e43edf3fccfa2dced3d3e.tar.gz caxlsx-53235912acc99ef8801e43edf3fccfa2dced3d3e.zip | |
remove crypto play
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/axlsx/util/cbf.rb | 333 | ||||
| -rw-r--r-- | lib/axlsx/util/ms_off_crypto.rb | 189 |
2 files changed, 0 insertions, 522 deletions
diff --git a/lib/axlsx/util/cbf.rb b/lib/axlsx/util/cbf.rb deleted file mode 100644 index f8fc057b..00000000 --- a/lib/axlsx/util/cbf.rb +++ /dev/null @@ -1,333 +0,0 @@ -# encoding: UTF-8 -module Axlsx - - # The Cfb class is a MS-OFF-CRYPTOGRAPHY specific OLE (MS-CBF) writer implementation. No attempt is made to re-invent the wheel for read/write of compound binary files. - class Cbf - - # the serialization for the CBF FAT - FAT_PACKING = "s128" - - # the serialization for the MS-OFF-CRYPTO version stream - VERSION_PACKING = 'l s30 l3' - - # The serialization for the MS-OFF-CRYPTO dataspace map stream - DATA_SPACE_MAP_PACKING = 'l6 s16 l s25 x2' - - # The serialization for the MS-OFF-CRYPTO strong encrytion data space stream - STRONG_ENCRYPTION_DATA_SPACE_PACKING = 'l3 s26' - - # The serialization for the MS-OFF-CRYPTO primary stream - PRIMARY_PACKING = 'l3 s38 l s40 l3 x12 l' - - # The cutoff size that determines if a stream should be in the mini-fat or the fat - MINI_CUTOFF = 4096 - - # The serialization for CBF header - HEADER_PACKING = "q x16 l s3 x10 l l x4 l*" - - # Creates a new Cbf object based on the ms_off_crypto object provided. - # @param [MsOffCrypto] ms_off_crypto - def initialize(ms_off_crypto) - @file_name = ms_off_crypto.file_name - @ms_off_crypto = ms_off_crypto - create_storages - mini_fat_stream - mini_fat - fat - header - end - - # creates or returns the version storage - # @return [Storage] - def version - @version ||= create_version - end - - # returns the data space map storage - # @return [Storage] - def data_space_map - @data_space_map ||= create_data_space_map - end - - # returns the primary storage - # @return [Storgae] - def primary - @primary ||= create_primary - end - - # returns the summary information storage - # @return [Storage] - def summary_information - @summary_information ||= create_summary_information - end - - # returns the document summary information - # @return [Storage] - def document_summary_information - @document_summary_information ||= create_document_summary_information - end - - # returns the stream of data allocated in the fat - # @return [String] - def fat_stream - @fat_stream ||= create_fat_stream - end - - # returns the stream allocated in the mini fat. - # return [String] - def mini_fat_stream - @mini_fat_stream ||= create_mini_fat_stream - end - - # returns the mini fat - # return [String] - def mini_fat - @mini_fat ||= create_mini_fat - end - - # returns the fat - # @return [String] - def fat - @fat ||= create_fat - end - - # returns the CFB header - # @return [String] - def header - @header ||= create_header - end - - # returns the encryption info from the ms_off_crypt object provided during intialization - # @return [String] encryption info - def encryption_info - @ms_off_crypto.encryption_info - end - - # returns the encrypted package from the ms_off_crypt object provided during initalization - # @return [String] encrypted package - def encrypted_package - @ms_off_crypto.encrypted_package - end - - # writes the compound binary file to disk - def save - ole = File.open(@file_name, 'w') - ole << header - ole << fat - @storages.each { |s| ole << s.to_s } - ole << Array.new((512-(ole.pos % 512)), 0).pack('c*') - ole << mini_fat - ole << mini_fat_stream - ole << fat_stream - ole.close - end - - private - - # Generates the storages required for ms-office-cryptography cfb - def create_storages - @storages = [] - @encryption_info = @ms_off_crypto.encryption_info - @encrypted_package = @ms_off_crypto.encrypted_package - - @storages << Storage.new('EncryptionInfo', :data=>encryption_info, :left=>3, :right=>11) # example shows right child. do we need the summary info???? - @storages << Storage.new('EncryptedPackage', :data=>encrypted_package, :color=>Storage::COLORS[:red]) - @storages << Storage.new([6].pack("c")+"DataSpaces", :child=>5, :modified =>129685612740945580, :created=>129685612740819979) - @storages << version - @storages << data_space_map - @storages << Storage.new('DataSpaceInfo', :right=>8, :child=>7, :created=>129685612740828880,:modified=>129685612740831800) - @storages << strong_encryption_data_space - @storages << Storage.new('TransformInfo', :color => Storage::COLORS[:red], :child=>9, :created=>129685612740834130, :modified=>129685612740943959) - @storages << Storage.new('StrongEncryptionTransform', :child=>10, :created=>129685612740834169, :modified=>129685612740942280) - @storages << primary - # @storages << summary_information - # @storages << document_summary_information - - # we do this at the end as we need to build the minifat stream to determine the size. #HOWEVER - it looks like the size should not include the padding? - @storages.unshift Storage.new('Root Entry', :type=>Storage::TYPES[:root], :color=>Storage::COLORS[:red], :child=>1, :data => mini_fat_stream) - - end - - # generates the mini fat stream - # @return [String] - def create_mini_fat_stream - mfs = [] - @storages.select{ |s| s.type == Storage::TYPES[:stream] && s.size < MINI_CUTOFF}.each_with_index do |stream, index| - puts "#{stream.name.pack('c*')}: #{stream.data.size}" - mfs.concat stream.data - mfs.concat Array.new(64 - (mfs.size % 64), 0) if mfs.size % 64 > 0 - puts "mini fat stream size: #{mfs.size}" - end - mfs.concat(Array.new(512 - (mfs.size % 512), 0)) - mfs.pack 'c*' - end - - # generates the fat stream. - # @return [String] - def create_fat_stream - mfs = [] - @storages.select{ |s| s.type == Storage::TYPES[:stream] && s.size >= MINI_CUTOFF}.each_with_index do |stream, index| - mfs.concat stream.data - mfs.concat Array.new(512 - (mfs.size % 512), 0) if mfs.size % 512 > 0 - end - mfs.pack 'c*' - end - - # creates the mini fat - # @return [String] - def create_mini_fat - v_mf = [] - @storages.select{ |s| s.type == Storage::TYPES[:stream] && s.size < MINI_CUTOFF}.each do |stream| - allocate_stream(v_mf, stream, 64) - end - v_mf.concat Array.new(128 - v_mf.size, -1) - v_mf.pack 'l*' - end - - # creates the fat - # @return [String] - def create_fat - v_fat = [-3] - # storages four per sector, allocation forces directories to start at sector ID 0 - allocate_stream(v_fat, @storages, 4) - # fat entry for minifat - allocate_stream(v_fat, 0, 512) - # fat entry for minifat stream - @storages[0].sector = v_fat.size - allocate_stream(v_fat, mini_fat_stream, 512) - # fat entries for encrypted package storage - # what to do about DIFAT for larger packages... - if @encrypted_package.size > (109 - v_fat.size) * 512 - raise ArgumentError, "Your package is too big!" - end - - if @encrypted_package.size >= MINI_CUTOFF - allocate_stream(v_fat, @encrypted_package, 512) - end - - v_fat.concat Array.new(128 - v_fat.size, -1) if v_fat.size < 128 #pack in unused sectors - v_fat.pack 'l*' - end - - # Creates the version storage - # @return [Storage] - def create_version - v_stream= [60, "Microsoft.Container.DataSpaces".bytes.to_a, 1, 1, 1].flatten!.pack VERSION_PACKING - Storage.new('Version', :data=>v_stream, :size=>v_stream.size) - end - - # returns the strong encryption data space storage - # @return [Storgae] - def strong_encryption_data_space - @strong_encryption_data_space ||= create_strong_encryption_data_space - end - - # Creates the data space map storage - # @return [Storgae] - def create_data_space_map - v_stream = [8,1,104, 1,0, 32, "EncryptedPackage".bytes.to_a, 50, "StrongEncryptionDataSpace".bytes.to_a].flatten!.pack DATA_SPACE_MAP_PACKING - Storage.new('DataSpaceMap', :data=>v_stream, :left => 4, :right => 6, :size=>v_stream.size) - end - - - # creates the stron encryption data space storage - # @return [Storgae] - def create_strong_encryption_data_space - v_stream = [8,1,50,"StrongEncryptionTransform".bytes.to_a,0].flatten.pack STRONG_ENCRYPTION_DATA_SPACE_PACKING - Storage.new("StrongEncryptionDataSpace", :data=>v_stream, :size => v_stream.size) - end - - # creates the primary storage - # @return [Storgae] - def create_primary - v_stream = [88,1,76,"{FF9A3F03-56EF-4613-BDD5-5A41C1D07246}".bytes.to_a].flatten - v_stream.concat [78, "Microsoft.Container.EncryptionTransform".bytes.to_a,0,1,1,1,4].flatten - v_stream = v_stream.pack PRIMARY_PACKING - Storage.new([6].pack("c")+"Primary", :data=>v_stream) - end - - - # creates the summary information storage - # @return [Storage] - def create_summary_information - v_stream = [] - v_stream.concat [0xFEFF, 0x0000, 0x030A, 0x0100, 0x0000, 0x0000, 0x0000, 0x0000] - v_stream.concat [0x0000, 0x0000, 0x0000, 0x0000, 0x0100, 0x0000, 0xE085, 0x9FF2] - v_stream.concat [0xF94F, 0x6810, 0xAB91, 0x0800, 0x2B27, 0xB3D9, 0x3000, 0x0000] - v_stream.concat [0xAC00, 0x0000, 0x0700, 0x0000, 0x0100, 0x0000, 0x4000, 0x0000] - v_stream.concat [0x0400, 0x0000, 0x4800, 0x0000, 0x0800, 0x0000, 0x5800, 0x0000] - v_stream.concat [0x1200, 0x0000, 0x6800, 0x0000, 0x0C00, 0x0000, 0x8C00, 0x0000] - v_stream.concat [0x0D00, 0x0000, 0x9800, 0x0000, 0x1300, 0x0000, 0xA400, 0x0000] - v_stream.concat [0x0200, 0x0000, 0xE9FD, 0x0000, 0x1E00, 0x0000, 0x0800, 0x0000] - v_stream.concat [0x7261, 0x6E64, 0x796D, 0x0000, 0x1E00, 0x0000, 0x0800, 0x0000] - v_stream.concat [0x7261, 0x6E64, 0x796D, 0x0000, 0x1E00, 0x0000, 0x1C00, 0x0000] - v_stream.concat [0x4D69, 0x6372, 0x6F73, 0x6F66, 0x7420, 0x4D61, 0x6369, 0x6E74] - v_stream.concat [0x6F73, 0x6820, 0x4578, 0x6365, 0x6C00, 0x0000, 0x4000, 0x0000] - v_stream.concat [0x10AC, 0x5396, 0x60BC, 0xCC01, 0x4000, 0x0000, 0x40F4, 0xFDAF] - v_stream.concat [0x60BC, 0xCC01, 0x0300, 0x0000, 0x0100, 0x0000] - - v_stream = v_stream.pack "s*" - - Storage.new([5].pack('c')+"SummaryInformation", :data=>v_stream, :left => 2) - end - - - # creates the document summary information storage - # @return [Storage] - def create_document_summary_information - v_stream = [] - v_stream.concat [0xFEFF, 0x0000, 0x030A, 0x0100, 0x0000, 0x0000, 0x0000, 0x0000] - v_stream.concat [0x0000, 0x0000, 0x0000, 0x0000, 0x0100, 0x0000, 0x02D5, 0xCDD5] - v_stream.concat [0x9C2E, 0x1B10, 0x9397, 0x0800, 0x2B2C, 0xF9AE, 0x3000, 0x0000] - v_stream.concat [0xCC00, 0x0000, 0x0900, 0x0000, 0x0100, 0x0000, 0x5000, 0x0000] - v_stream.concat [0x0F00, 0x0000, 0x5800, 0x0000, 0x1700, 0x0000, 0x6400, 0x0000] - v_stream.concat [0x0B00, 0x0000, 0x6C00, 0x0000, 0x1000, 0x0000, 0x7400, 0x0000] - v_stream.concat [0x1300, 0x0000, 0x7C00, 0x0000, 0x1600, 0x0000, 0x8400, 0x0000] - v_stream.concat [0x0D00, 0x0000, 0x8C00, 0x0000, 0x0C00, 0x0000, 0x9F00, 0x0000] - v_stream.concat [0x0200, 0x0000, 0xE9FD, 0x0000, 0x1E00, 0x0000, 0x0400, 0x0000] - v_stream.concat [0x0000, 0x0000, 0x0300, 0x0000, 0x0000, 0x0C00, 0x0B00, 0x0000] - v_stream.concat [0x0000, 0x0000, 0x0B00, 0x0000, 0x0000, 0x0000, 0x0B00, 0x0000] - v_stream.concat [0x0000, 0x0000, 0x0B00, 0x0000, 0x0000, 0x0000, 0x1E10, 0x0000] - v_stream.concat [0x0100, 0x0000, 0x0700, 0x0000, 0x5368, 0x6565, 0x7431, 0x000C] - v_stream.concat [0x1000, 0x0002, 0x0000, 0x001E, 0x0000, 0x0013, 0x0000, 0x00E3] - v_stream.concat [0x83AF, 0xE383, 0xBCE3, 0x82AF, 0xE382, 0xB7E3, 0x83BC, 0xE383] - v_stream.concat [0x8800, 0x0300, 0x0000, 0x0100, 0x0000, 0x0000] - v_stream = v_stream.pack 'c*' - Storage.new([5].pack('c')+"DocumentSummaryInformation", :data=>v_stream) - end - - # Creates the header - # @return [String] - def create_header - header = [] - header << -2226271756974174256 # identifier pack as q - header << 196670 # version pack as L - header << 65534 # byte order pack as s - header << 9 # sector shift - header << 6 # mini-sector shift - header << (fat.size/512.0).ceil # this is the number of FAT sectors in the file at index 6 pack as L - header << header.last # this is the first directory sector, index of 7 pack as L - header << MINI_CUTOFF # minfat cutoff pack as L - # MiniFat starts after directories - header << (fat.size/512.0).ceil + (@storages.size/4.0).ceil # this is the sector id for the first minifat index 10 pack as L - header << (mini_fat.size/512.0).ceil # minifat sector count index 11 pack as L - header << -2 # the first DIFAT - set to end of chain until we exceed a single FAT pack as L - header << 0 # number of DIFAT sectors, unless we go beyond 109 FAT sectors this will always be 0 pack as L - header << 0 # first FAT sector defined in the DIFAT pack as L - header.concat Array.new(108, -1) # Difat sectors pack as L108 - header.pack(HEADER_PACKING) - end - - # Allocates sector chains in a allocation table based on the sector size and stream provided - # If a storage obeject is provided, the starting sector value for the storage is updated based on the allocation performed here. - # @param [Array] table Allocation table array - # @param [Storage | String] stream - # @param [Integer] size The cutoff size for the stream. - def allocate_stream(table, stream, size) - stream.sector = table.size if stream.respond_to?(:sector) - ((stream.size / size.to_f).ceil).times { table << table.size } - table[table.size-1] = -2 # this is the CBF chain terminator - end - - end -end diff --git a/lib/axlsx/util/ms_off_crypto.rb b/lib/axlsx/util/ms_off_crypto.rb deleted file mode 100644 index e91bb20f..00000000 --- a/lib/axlsx/util/ms_off_crypto.rb +++ /dev/null @@ -1,189 +0,0 @@ -# encoding: UTF-8 -require 'digest' -require 'base64' -require 'openssl' - -module Axlsx - - # The MsOffCrypto class implements ECMA-367 encryption based on the MS-OFF-CRYPTO specification - class MsOffCrypto - - # Creates a new MsOffCrypto Object - # @param [String] file_name the location of the file you want to encrypt - # @param [String] pwd the password to use when encrypting the file - def initialize(file_name, pwd) - self.password = pwd - self.file_name = file_name - end - - # Generates a new CBF file based on this instance of ms-off-crypto and overwrites the unencrypted file. - def save - cfb = Cbf.new(self) - cfb.save - end - - # returns the raw password used in encryption - # @return [String] - attr_reader :password - - # sets the password to be used for encryption - # @param [String] v the password, @default 'password' - # @return [String] - def password=(v) - @password = v || 'password' - end - - # retruns the file name of the archive to be encrypted - # @return [String] - attr_reader :file_name - - # sets the filename - # @return [String] - def file_name=(v) - #TODO verfify that the file specified exists and is an unencrypted xlsx archive - @file_name = v - end - - - # encrypts and returns the package specified by the file name - # @return [String] - def encrypted_package - @encrypted_package ||= encrypt_package(file_name) - end - - # returns the encryption info for this instance of ms-off-crypto - # @return [String] - def encryption_info - @encryption_info ||= create_encryption_info - end - - # returns a random salt - # @return [String] - def salt - @salt ||= Digest::SHA1.digest(rand(16**16).to_s) - end - - # returns a random verifier - # @return [String] - def verifier - @verifier ||= rand(16**16).to_s - end - - # returns the verifier encrytped - # @return [String] - def encrypted_verifier - @encrypted_verifier ||= encrypt(verifier) - end - - # returns the verifier hash encrypted - # @return [String] - def encrypted_verifier_hash - @encrypted_verifier_hash ||= encrypt(verifier_hash) - end - - # returns a verifier hash - # @return [String] - def verifier_hash - @verifier_hash ||= create_verifier_hash - end - - # returns an encryption key - # @return [String] - def key - @key ||= create_key - end - - # size of unencrypted package? concated with encrypted package - def encrypt_package(file_name) - package = File.open(file_name, 'r') - crypt_pack = encrypt(package.read) - [crypt_pack.size].pack('q') + crypt_pack - end - - # Generates an encryption info structure - # @return [String] - 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 - - # 2.3.3 - def create_verifier_hash - vh = Digest::SHA1.digest(verifier) - vh << Array.new(32 - vh.size, 0).join('') - end - - # 2.3.4.7 ECMA-376 Document Encryption Key Generation (Standard Encryption) - 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 - - # ensures that the a hashed decryption of the encryption verifier matches the decrypted verifier hash. - # @return [Boolean] - def verify_password - v = Digest::SHA1.digest decrypt(@encrypted_verifier) - vh = decrypt(@encrypted_verifier_hash) - vh[0..15] == v[0..15] - end - - # encrypts the data proved - # @param [String] data - # @return [String] the encrypted data - def encrypt(data) - aes = OpenSSL::Cipher.new("AES-128-ECB") - aes.encrypt - aes.key = key - aes.update(data) << aes.final - end - - # dencrypts the data proved - # @param [String] data - # @return [String] the dencrypted data - def decrypt(data) - aes = OpenSSL::Cipher.new("AES-128-ECB") - aes.decrypt - aes.key = key - aes.update(data) << aes.final - end - - end -end |
