lib/app_info/android/signatures/info.rb in app-info-3.0.0.beta1 vs lib/app_info/android/signatures/info.rb in app-info-3.0.0.beta2

- old
+ new

@@ -1,152 +1,158 @@ # frozen_string_literal: true require 'stringio' require 'openssl' -module AppInfo::Android::Signature - # APK signature scheme signurate info - # - # FORMAT: - # OFFSET DATA TYPE DESCRIPTION - # * @+0 bytes uint64: size in bytes (excluding this field) - # * @+8 bytes payload - # * @-24 bytes uint64: size in bytes (same as the one above) - # * @-16 bytes uint128: magic value - class Info - include AppInfo::Helper::IOBlock +module AppInfo + class Android < File + module Signature + # APK signature scheme signurate info + # + # FORMAT: + # OFFSET DATA TYPE DESCRIPTION + # * @+0 bytes uint64: size in bytes (excluding this field) + # * @+8 bytes payload + # * @-24 bytes uint64: size in bytes (same as the one above) + # * @-16 bytes uint128: magic value + class Info + include AppInfo::Helper::IOBlock - # Signature block information - SIG_SIZE_OF_BLOCK_SIZE = 8 - SIG_MAGIC_BLOCK_SIZE = 16 - SIG_BLOCK_MIN_SIZE = 32 + # Signature block information + SIG_SIZE_OF_BLOCK_SIZE = 8 + SIG_MAGIC_BLOCK_SIZE = 16 + SIG_BLOCK_MIN_SIZE = 32 - # Magic value: APK Sig Block 42 - SIG_MAGIC = [ - 0x41, 0x50, 0x4b, 0x20, 0x53, 0x69, - 0x67, 0x20, 0x42, 0x6c, 0x6f, 0x63, - 0x6b, 0x20, 0x34, 0x32 - ].freeze + # Magic value: APK Sig Block 42 + SIG_MAGIC = [ + 0x41, 0x50, 0x4b, 0x20, 0x53, 0x69, + 0x67, 0x20, 0x42, 0x6c, 0x6f, 0x63, + 0x6b, 0x20, 0x34, 0x32 + ].freeze - attr_reader :total_size, :pairs, :magic, :logger + attr_reader :total_size, :pairs, :magic, :logger - def initialize(version, parser, logger) - @version = version - @parser = parser - @logger = logger + def initialize(version, parser, logger) + @version = version + @parser = parser + @logger = logger - pares_signatures_pairs - end - - # Find singers - # - # FORMAT: - # OFFSET DATA TYPE DESCRIPTION - # * @+0 bytes uint64: size in bytes - # * @+8 bytes payload block - # * @+0 bytes uint32: id - # * @+4 bytes payload: value - def signers(block_id) - count = 0 - until @pairs.eof? - left_bytes = left_bytes_check( - @pairs, UINT64_SIZE, NotFoundError, - "Insufficient data to read size of APK Signing Block ##{count}" - ) - - pair_buf = @pairs.read(UINT64_SIZE) - pair_size = pair_buf.unpack1('Q') - if pair_size < UINT32_SIZE || pair_size > UINT32_MAX_VALUE - raise NotFoundError, - "APK Signing Block ##{count} size out of range: #{pair_size} > #{UINT32_MAX_VALUE}" + pares_signatures_pairs end - if pair_size > left_bytes - raise NotFoundError, - "APK Signing Block ##{count} size out of range: #{pair_size} > #{left_bytes}" - end + # Find singers + # + # FORMAT: + # OFFSET DATA TYPE DESCRIPTION + # * @+0 bytes uint64: size in bytes + # * @+8 bytes payload block + # * @+0 bytes uint32: id + # * @+4 bytes payload: value + def signers(block_id) + count = 0 + until @pairs.eof? + left_bytes = left_bytes_check( + @pairs, UINT64_SIZE, NotFoundError, + "Insufficient data to read size of APK Signing Block ##{count}" + ) - # fetch next signer block position - next_pos = @pairs.pos + pair_size.to_i + pair_buf = @pairs.read(UINT64_SIZE) + pair_size = pair_buf.unpack1('Q') + if pair_size < UINT32_SIZE || pair_size > UINT32_MAX_VALUE + raise NotFoundError, + "APK Signing Block ##{count} size out of range: #{pair_size} > #{UINT32_MAX_VALUE}" + end - id_block = @pairs.read(UINT32_SIZE) - id_bytes = id_block.unpack('C*') - if id_bytes == block_id - logger.debug "Signature block id v#{@version} scheme (0x#{id_block.unpack1('H*')}) found" - value = @pairs.read(pair_size - UINT32_SIZE) - return StringIO.new(value) - end + if pair_size > left_bytes + raise NotFoundError, + "APK Signing Block ##{count} size out of range: #{pair_size} > #{left_bytes}" + end - @pairs.seek(next_pos) - count += 1 - end + # fetch next signer block position + next_pos = @pairs.pos + pair_size.to_i - block_id_hex = block_id.reverse.pack('C*').unpack1('H*') - raise NotFoundError, "Not found block id 0x#{block_id_hex} in APK Signing Block." - end + id_block = @pairs.read(UINT32_SIZE) + id_bytes = id_block.unpack('C*') + if id_bytes == block_id + logger.debug "Signature block id v#{@version} scheme (0x#{id_block.unpack1('H*')}) found" + value = @pairs.read(pair_size - UINT32_SIZE) + return StringIO.new(value) + end - def zip64? - zip_io.zip64_file?(start_buffer) - end + @pairs.seek(next_pos) + count += 1 + end - def pares_signatures_pairs - block = signature_block - block.rewind - # get pairs size - @total_size = block.size - (SIG_SIZE_OF_BLOCK_SIZE + SIG_MAGIC_BLOCK_SIZE) + block_id_hex = block_id.reverse.pack('C*').unpack1('H*') + raise NotFoundError, "Not found block id 0x#{block_id_hex} in APK Signing Block." + end - # get pairs block - @pairs = StringIO.new(block.read(@total_size)) + def zip64? + zip_io.zip64_file?(start_buffer) + end - # get magic value - block.seek(block.pos + SIG_SIZE_OF_BLOCK_SIZE) - @magic = block.read(SIG_MAGIC_BLOCK_SIZE) - end + def pares_signatures_pairs + block = signature_block + block.rewind + # get pairs size + @total_size = block.size - (SIG_SIZE_OF_BLOCK_SIZE + SIG_MAGIC_BLOCK_SIZE) - def signature_block - @signature_block ||= lambda { - logger.debug "cdir_offset: #{cdir_offset}" + # get pairs block + @pairs = StringIO.new(block.read(@total_size)) - file_io.seek(cdir_offset - (Info::SIG_MAGIC_BLOCK_SIZE + Info::SIG_SIZE_OF_BLOCK_SIZE)) - footer_block = file_io.read(Info::SIG_SIZE_OF_BLOCK_SIZE) - if footer_block.size < Info::SIG_SIZE_OF_BLOCK_SIZE - raise NotFoundError, "APK Signing Block size out of range: #{footer_block.size}" + # get magic value + block.seek(block.pos + SIG_SIZE_OF_BLOCK_SIZE) + @magic = block.read(SIG_MAGIC_BLOCK_SIZE) end - footer = footer_block.unpack1('Q') - total_size = footer - offset = cdir_offset - total_size - Info::SIG_SIZE_OF_BLOCK_SIZE - raise NotFoundError, "APK Signing Block offset out of range: #{offset}" if offset.negative? + def signature_block + @signature_block ||= lambda { + logger.debug "cdir_offset: #{cdir_offset}" - file_io.seek(offset) - header = file_io.read(Info::SIG_SIZE_OF_BLOCK_SIZE).unpack1('Q') + file_io.seek(cdir_offset - (Info::SIG_MAGIC_BLOCK_SIZE + Info::SIG_SIZE_OF_BLOCK_SIZE)) + footer_block = file_io.read(Info::SIG_SIZE_OF_BLOCK_SIZE) + if footer_block.size < Info::SIG_SIZE_OF_BLOCK_SIZE + raise NotFoundError, "APK Signing Block size out of range: #{footer_block.size}" + end - if header != footer - raise NotFoundError, - "APK Signing Block header and footer mismatch: #{header} != #{footer}" - end + footer = footer_block.unpack1('Q') + total_size = footer + offset = cdir_offset - total_size - Info::SIG_SIZE_OF_BLOCK_SIZE + if offset.negative? + raise NotFoundError, "APK Signing Block offset out of range: #{offset}" + end - io = file_io.read(total_size) - StringIO.new(io) - }.call - end + file_io.seek(offset) + header = file_io.read(Info::SIG_SIZE_OF_BLOCK_SIZE).unpack1('Q') - def cdir_offset - @cdir_offset ||= lambda { - eocd_buffer = zip_io.get_e_o_c_d(start_buffer) - eocd_buffer[12..16].unpack1('V') - }.call - end + if header != footer + raise NotFoundError, + "APK Signing Block header and footer mismatch: #{header} != #{footer}" + end - def start_buffer - @start_buffer ||= zip_io.start_buf(file_io) - end + io = file_io.read(total_size) + StringIO.new(io) + }.call + end - def zip_io - @zip_io ||= @parser.zip - end + def cdir_offset + @cdir_offset ||= lambda { + eocd_buffer = zip_io.get_e_o_c_d(start_buffer) + eocd_buffer[12..16].unpack1('V') + }.call + end - def file_io - @file_io ||= File.open(@parser.file, 'rb') + def start_buffer + @start_buffer ||= zip_io.start_buf(file_io) + end + + def zip_io + @zip_io ||= @parser.zip + end + + def file_io + @file_io ||= ::File.open(@parser.file, 'rb') + end + end end end end