class AmpkReader
Attributes
items[R]
path[R]
Public Class Methods
new(path, public_key)
click to toggle source
# File lib/ampk/reader.rb, line 5 def initialize(path, public_key) @fp = File.open(@path=path,'rb') @public_key = public_key end
Public Instance Methods
close()
click to toggle source
# File lib/ampk/reader.rb, line 72 def close @fp.close end
entities()
click to toggle source
# File lib/ampk/reader.rb, line 38 def entities auto_scan @items.map { |item| item[:name] } end
entity(name)
click to toggle source
# File lib/ampk/reader.rb, line 60 def entity(name) auto_scan item = @items.find { |item| item[:name] == name } raise Errno::ENOENT, name if item.nil? item end
entity_compressed?(name)
click to toggle source
# File lib/ampk/reader.rb, line 49 def entity_compressed?(name) item = entity(name) item.has_key?(:filter) and item[:filter].include?(?Z) end
entity_encrypted?(name)
click to toggle source
# File lib/ampk/reader.rb, line 45 def entity_encrypted?(name) item = entity(name) item.has_key?(:filter) and item[:filter].include?(?C) end
entity_signed?(name)
click to toggle source
# File lib/ampk/reader.rb, line 42 def entity_signed?(name) entity(name).has_key?(:signature) end
entity_size(name)
click to toggle source
# File lib/ampk/reader.rb, line 53 def entity_size(name) entity(name)[:length] end
Also aliased as: entity_length
entity_type(name)
click to toggle source
# File lib/ampk/reader.rb, line 57 def entity_type(name) entity(name)[:type] end
read_entity(name)
click to toggle source
# File lib/ampk/reader.rb, line 66 def read_entity(name) auto_scan unless @items item = @items.find { |item| item[:name] == name } raise Errno::ENOENT, name if item.nil? read_entity_data(item) end
verify!(verify_data=false)
click to toggle source
# File lib/ampk/reader.rb, line 9 def verify!(verify_data=false) @fp.rewind must('start AMPK') { read4 == 'AMPK' } until @fp.eof? break if peek4 == 'ENDS' name = verify_entry('NAME', true) headers = {} signature = nil until (header = peek4).eql?('DATA') headers[header] ||= 0 headers[header] += 1 raise "Duplicate #{header} entry for #{name}" if headers[header] > 1 case header when 'FILT', 'DLEN', 'TYPE' verify_entry(header, false) when 'SIGN' signature = verify_entry('SIGN', true) end end if signature verify_entry_with_sig('DATA', verify_data, signature) else verify_entry('DATA', verify_data) end end end
Protected Instance Methods
auto_scan()
click to toggle source
# File lib/ampk/reader.rb, line 77 def auto_scan read_headers unless @items nil end
decode(item, data)
click to toggle source
# File lib/ampk/reader.rb, line 144 def decode(item, data) if item[:encoding] && data.respond_to?(:encoding) data.force_encoding(item[:encoding]) else data end end
get_byte()
click to toggle source
# File lib/ampk/reader.rb, line 210 def get_byte if @fp.respond_to?(:getbyte) @fp.getbyte else @fp.getc end end
must(description) { || ... }
click to toggle source
# File lib/ampk/reader.rb, line 186 def must(description,&block) raise RuntimeError, "Archive is invalid: It must #{description}" unless yield end
peek4()
click to toggle source
# File lib/ampk/reader.rb, line 195 def peek4 pos = @fp.tell str = @fp.read(4) @fp.seek(pos, IO::SEEK_SET) str end
read4()
click to toggle source
# File lib/ampk/reader.rb, line 190 def read4 str = @fp.read(4) str end
read_data()
click to toggle source
# File lib/ampk/reader.rb, line 218 def read_data size = read_data_size data = @fp.read(size) [size, data] end
read_data_size()
click to toggle source
# File lib/ampk/reader.rb, line 202 def read_data_size size = "" while (c = get_byte).nonzero? size << c end size.to_i end
read_entity_data(item)
click to toggle source
# File lib/ampk/reader.rb, line 118 def read_entity_data(item) @fp.seek(item[:offset]) data = read_entry('DATA') if item[:filter] item[:filter].reverse.each_byte do |byte| case byte when ?Z.ord data = Zlib::Inflate.inflate(data) when ?C.ord data = @public_key.decrypt(data) end end end if item[:signature] if @public_key.verify(item[:signature], data) return decode(item, data) else raise RuntimeError, "Invalid data for #{item[:name]}" end else return decode(item, data) end end
read_entity_header()
click to toggle source
# File lib/ampk/reader.rb, line 94 def read_entity_header start = @fp.tell name = read_entry('NAME') item = { :name => name, :header => start } until (header=peek4).eql?('DATA') case header when 'SIGN' item[:signature] = read_entry(header) when 'DLEN' item[:length] = read_entry(header).to_i when 'FILT' item[:filter] = read_entry(header) when 'TYPE' item[:type] = read_entry(header) when 'ENCD' item[:encoding] = read_entry(header) else raise RuntimeError, "Unknown/invalid header #{header}" end end item[:offset] = @fp.tell item end
read_entry(name)
click to toggle source
# File lib/ampk/reader.rb, line 152 def read_entry(name) must("have a #{name} entry") { read4 == name } size, data = read_data must("have some #{name} data") { size == data.size } data end
read_headers()
click to toggle source
# File lib/ampk/reader.rb, line 82 def read_headers @fp.rewind must('start AMPK') { read4 == 'AMPK' } @items = [] until @fp.eof? break if peek4 == 'ENDS' @items << read_entity_header() skip_entry('DATA') end @items end
skip_data()
click to toggle source
# File lib/ampk/reader.rb, line 224 def skip_data size = read_data_size posn = @fp.tell @fp.seek(size, IO::SEEK_CUR) skipped = @fp.tell - posn [size, skipped] end
skip_entry(name)
click to toggle source
# File lib/ampk/reader.rb, line 159 def skip_entry(name) must("have a #{name} entry'") { read4 == name } size, skipped = skip_data must("have some #{name} data") { size == skipped } nil end
verify_entry(name, verify_data)
click to toggle source
# File lib/ampk/reader.rb, line 166 def verify_entry(name, verify_data) must("have a #{name} entry") { read4 == name } if verify_data size, data = read_data must("have some #{name} data") { size == data.size } data else size, skipped = skip_data must("have at least as much data left") { skipped == size } nil end end
verify_entry_with_sig(name, verify_data, sig)
click to toggle source
# File lib/ampk/reader.rb, line 179 def verify_entry_with_sig(name, verify_data, sig) data = verify_entry(name, verify_data) if data and sig must('be valid, signed data') { @public_key.verify(sig, data) } end end