diff options
author | o <o@immerda.ch> | 2011-06-25 20:24:48 +0200 |
---|---|---|
committer | o <o@immerda.ch> | 2011-06-25 20:24:48 +0200 |
commit | f8eb36b0eb9c7c3848109c59d992f32f76733d33 (patch) | |
tree | b8f073c88d82a129977c95fcb4b8007424ba7f7e | |
parent | c2eae6fa1ee87599307321b9d432e4148f41c228 (diff) |
setup some parsing infrastructure.
the plan is to have classes fore every packet type and (in a separate
module) the same classes again, but extended by a parsing module such that
each packet can parse itself.
detect first packet headers and parse its size.
can be used e.g. with:
FPG::Parse::Parser.parse( File.open( "spec/fixtures/test.bin.pub" ) )
will create a Signature Packet with size 13
-rw-r--r-- | lib/fpg/io/radix64.rb | 180 | ||||
-rw-r--r-- | lib/fpg/packets/new_packet.rb | 7 | ||||
-rw-r--r-- | lib/fpg/packets/signature.rb | 6 | ||||
-rw-r--r-- | lib/fpg/parse/_packet_headers.rb | 51 | ||||
-rw-r--r-- | lib/fpg/parse/new_packet.rb | 21 | ||||
-rw-r--r-- | lib/fpg/parse/new_packet_header.rb | 23 | ||||
-rw-r--r-- | lib/fpg/parse/parser.rb | 10 | ||||
-rw-r--r-- | lib/fpg/parse/signature.rb | 10 | ||||
-rw-r--r-- | spec/radix64_spec.rb | 3 |
9 files changed, 221 insertions, 90 deletions
diff --git a/lib/fpg/io/radix64.rb b/lib/fpg/io/radix64.rb index 49102a0..4ecdaf3 100644 --- a/lib/fpg/io/radix64.rb +++ b/lib/fpg/io/radix64.rb @@ -1,101 +1,103 @@ module FPG - class Radix64 - def char_table - @char_table ||= "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".each_byte.collect.to_a - end - def pad - pad_s.unpack("C1!").first - end - def pad_s - "=" - end - def char2six_table - @char2six_table ||= Hash[char_table.zip((0..63).to_a)] - end - def char2six(char) - char2six_table[char] - end - def six2char(six) - char_table[six] - end - def quad2bytes(quad) - q = (quad.unpack("C4!").collect { |v| char2six(v) }).to_a - [ (q[0]<<2)+(q[1]>>4), ((q[1]<<4)%256)+(q[2]>>2), ((q[2]<<6)%256)+q[3] ] - end - def padded_quad2bytes(quad) - if quad[2] == pad_s then - q = quad.unpack("C2!").collect { |v| char2six(v) } - [ (q[0]<<2)+(q[1]>>4), ((q[1]<<4)%256)>>4 ] - else - q = quad.unpack("C3!").collect { |v| char2six(v) } - [ (q[0]<<2)+(q[1]>>4), ((q[1]<<4)%256)+(q[2]>>2), ((q[2]<<6)%256) ] - end - end - def bytes2quad(bytes) - [ six2char(bytes[0]>>2), six2char(((bytes[0]<<4)%64)+(bytes[1]>>4)), - six2char(((bytes[1]<<2)%64)+(bytes[2]>>6)), six2char(bytes[2]%64) ].pack("C4!") - end - def bytes2padded_quad(bytes) - if bytes.length == 1 - [six2char(bytes[0]>>2), six2char((bytes[0]<<4)%64), pad, pad].pack("C4!") - elsif bytes.length == 2 - [ six2char(bytes[0]>>2), six2char(((bytes[0]<<4)%64)+(bytes[1]>>4)), - six2char((bytes[1]<<2)%64), pad].pack("C4!") - end - end - def de_armor( stream ) - stream.each do |line| - line.chop! - return if line.length == 0 - 0.step(line.length-1, 4) do |i| - quad = line[i..i+3] - if quad[3] == pad_s - bytes = padded_quad2bytes(quad) - if bytes.length == 2 and bytes[1] != 0 - yield bytes[0] - yield bytes[1] - elsif bytes[0] != 0 - yield bytes[0] + module Io + class Radix64 + def char_table + @char_table ||= "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".each_byte.collect.to_a + end + def pad + pad_s.unpack("C1!").first + end + def pad_s + "=" + end + def char2six_table + @char2six_table ||= Hash[char_table.zip((0..63).to_a)] + end + def char2six(char) + char2six_table[char] + end + def six2char(six) + char_table[six] + end + def quad2bytes(quad) + q = (quad.unpack("C4!").collect { |v| char2six(v) }).to_a + [ (q[0]<<2)+(q[1]>>4), ((q[1]<<4)%256)+(q[2]>>2), ((q[2]<<6)%256)+q[3] ] + end + def padded_quad2bytes(quad) + if quad[2] == pad_s then + q = quad.unpack("C2!").collect { |v| char2six(v) } + [ (q[0]<<2)+(q[1]>>4), ((q[1]<<4)%256)>>4 ] + else + q = quad.unpack("C3!").collect { |v| char2six(v) } + [ (q[0]<<2)+(q[1]>>4), ((q[1]<<4)%256)+(q[2]>>2), ((q[2]<<6)%256) ] + end + end + def bytes2quad(bytes) + [ six2char(bytes[0]>>2), six2char(((bytes[0]<<4)%64)+(bytes[1]>>4)), + six2char(((bytes[1]<<2)%64)+(bytes[2]>>6)), six2char(bytes[2]%64) ].pack("C4!") + end + def bytes2padded_quad(bytes) + if bytes.length == 1 + [six2char(bytes[0]>>2), six2char((bytes[0]<<4)%64), pad, pad].pack("C4!") + elsif bytes.length == 2 + [ six2char(bytes[0]>>2), six2char(((bytes[0]<<4)%64)+(bytes[1]>>4)), + six2char((bytes[1]<<2)%64), pad].pack("C4!") + end + end + def de_armor( stream ) + stream.each do |line| + line.chop! + return if line.length == 0 + 0.step(line.length-1, 4) do |i| + quad = line[i..i+3] + if quad[3] == pad_s + bytes = padded_quad2bytes(quad) + if bytes.length == 2 and bytes[1] != 0 + yield bytes[0] + yield bytes[1] + elsif bytes[0] != 0 + yield bytes[0] + end + return end - return + bytes = quad2bytes(quad) + yield bytes[0] + yield bytes[1] + yield bytes[2] end - bytes = quad2bytes(quad) - yield bytes[0] - yield bytes[1] - yield bytes[2] end end - end - def armor( stream ) - line_buffer = "" - line_length = 0 - buffer = [0,0,0] - buffer_size = 0 - stream.each_byte do |byte| - if buffer_size == 3 then - line_buffer << bytes2quad(buffer) - line_length += 4 - buffer_size = 0 + def armor( stream ) + line_buffer = "" + line_length = 0 + buffer = [0,0,0] + buffer_size = 0 + stream.each_byte do |byte| + if buffer_size == 3 then + line_buffer << bytes2quad(buffer) + line_length += 4 + buffer_size = 0 + end + if line_length > 63 then + yield line_buffer + line_length = 0 + line_buffer = "" + end + buffer[buffer_size] = byte + buffer_size += 1 end - if line_length > 63 then - yield line_buffer - line_length = 0 - line_buffer = "" + if buffer_size == 1 + line_buffer << bytes2padded_quad([buffer[0]]) + elsif buffer_size == 2 + line_buffer << bytes2padded_quad([buffer[0],buffer[1]]) end - buffer[buffer_size] = byte - buffer_size += 1 + yield line_buffer end - if buffer_size == 1 - line_buffer << bytes2padded_quad([buffer[0]]) - elsif buffer_size == 2 - line_buffer << bytes2padded_quad([buffer[0],buffer[1]]) + def armor_public_key( stream ) + yield Versioning.public_key_header + armor( stream ) + yield Versioning.public_key_footer end - yield line_buffer - end - def armor_public_key( stream ) - yield Versioning.public_key_header - armor( stream ) - yield Versioning.public_key_footer end end end diff --git a/lib/fpg/packets/new_packet.rb b/lib/fpg/packets/new_packet.rb new file mode 100644 index 0000000..417c737 --- /dev/null +++ b/lib/fpg/packets/new_packet.rb @@ -0,0 +1,7 @@ +module FPG + module Packets + class NewPacket + attr_accessor :byte_size + end + end +end diff --git a/lib/fpg/packets/signature.rb b/lib/fpg/packets/signature.rb new file mode 100644 index 0000000..f8f4aef --- /dev/null +++ b/lib/fpg/packets/signature.rb @@ -0,0 +1,6 @@ +module FPG + module Packets + class Signature < NewPacket + end + end +end diff --git a/lib/fpg/parse/_packet_headers.rb b/lib/fpg/parse/_packet_headers.rb new file mode 100644 index 0000000..a016a04 --- /dev/null +++ b/lib/fpg/parse/_packet_headers.rb @@ -0,0 +1,51 @@ +module FPG + module Parse + module PacketHeaders + def packet_for( byte ) + case byte + when 0 + raise Error, "a packet tag must not be 0" + when 1 + PublicKeyEncryptedSessionKey.new + when 2 + Signature.new + when 3 + SymmetricKeyEncryptedSession.new + when 4 + OnePassSignature.new + when 5 + SecretKey.new + when 6 + PublicKey.new + when 7 + SecretSubkey.new + when 8 + CompressedData.new + when 9 + SymmetricallyEncryptedData.new + when 10 + Marker.new + when 11 + Literal.new + when 12 + Trust.new + when 13 + UserId.new + when 14 + PublicSubkey.new + when 17 + UserAttribute.new + when 18 + SymmetricEncryptedAndIntegrityProtectedData.new + when 19 + MudificationDetectionCode.new + when 60..63 + raise Error, 'Private or Experimental Header detected' + else + nil + end + end + end + end +end + diff --git a/lib/fpg/parse/new_packet.rb b/lib/fpg/parse/new_packet.rb new file mode 100644 index 0000000..f4e7658 --- /dev/null +++ b/lib/fpg/parse/new_packet.rb @@ -0,0 +1,21 @@ +module FPG + module Parse + module NewPacketParser + def parse( stream ) + size= packet_size( stream ) + puts "i'm size "+size.to_s + stream.seek( size, IO::SEEK_CUR ) + end + def packet_size( str ) + tag = str.getbyte + return tag if tag < 192 + return ((tag-192)<<8) + str.getbyte + 192 if tag < 224 + return (str.getbyte<<24) | (str.getbyte<<16) | (str.getbyte<<8) | str.getbyte if tag == 255 + raise Error, "Partial Body lengths are not supported" + end + end + class NewPacket < Packets::NewPacket + include NewPacketParser + end + end +end diff --git a/lib/fpg/parse/new_packet_header.rb b/lib/fpg/parse/new_packet_header.rb new file mode 100644 index 0000000..a65a82e --- /dev/null +++ b/lib/fpg/parse/new_packet_header.rb @@ -0,0 +1,23 @@ +module FPG + module Parse + class NewPacketHeader + include PacketHeaders + def parse( stream ) + stream.each_byte do |byte| + current_packet = packet_for( byte ) + unless current_packet.nil? then + puts current_packet.inspect + return current_packet.parse( stream ) + end + end + end + def packet_size( str ) + tag = str.getbyte + return tag if tag < 192 + return ((tag-192)<<8) + str.getbyte + 192 if tag < 224 + return (str.getbyte<<24) | (str.getbyte<<16) | (str.getbyte<<8) | str.getbyte if tag == 255 + raise Error, "Partial Body lengths are not supported" + end + end +end +end diff --git a/lib/fpg/parse/parser.rb b/lib/fpg/parse/parser.rb new file mode 100644 index 0000000..3d0cd26 --- /dev/null +++ b/lib/fpg/parse/parser.rb @@ -0,0 +1,10 @@ +module FPG + module Parse + class Parser + def self.parse( stream ) + #TODO: parse until stream at end + NewPacketHeader.new.parse( stream ) + end + end + end +end diff --git a/lib/fpg/parse/signature.rb b/lib/fpg/parse/signature.rb new file mode 100644 index 0000000..41bee87 --- /dev/null +++ b/lib/fpg/parse/signature.rb @@ -0,0 +1,10 @@ +module FPG + module Parse + module SignatureParser + include NewPacketParser + end + class Signature < Packets::Signature + include SignatureParser + end + end +end diff --git a/spec/radix64_spec.rb b/spec/radix64_spec.rb index 73262d1..81c9bb6 100644 --- a/spec/radix64_spec.rb +++ b/spec/radix64_spec.rb @@ -1,8 +1,9 @@ - require File.join(File.dirname(__FILE__), %w[spec_helper]) require 'stringio' +include FPG::Io + describe Radix64 do before(:each) do @radix = Radix64.new |