summaryrefslogtreecommitdiffhomepage
path: root/lib
diff options
context:
space:
mode:
authorRandy Morgan <[email protected]>2012-06-20 08:21:06 +0900
committerRandy Morgan <[email protected]>2012-06-20 08:21:06 +0900
commit53235912acc99ef8801e43edf3fccfa2dced3d3e (patch)
tree5534d319259a40b1404f19a2b1c8de8252a329b0 /lib
parent16a7fd2450581353efc9ec0daf267b28ea87fe02 (diff)
downloadcaxlsx-53235912acc99ef8801e43edf3fccfa2dced3d3e.tar.gz
caxlsx-53235912acc99ef8801e43edf3fccfa2dced3d3e.zip
remove crypto play
Diffstat (limited to 'lib')
-rw-r--r--lib/axlsx/util/cbf.rb333
-rw-r--r--lib/axlsx/util/ms_off_crypto.rb189
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