summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoro <o@immerda.ch>2011-06-30 20:28:19 +0200
committero <o@immerda.ch>2011-06-30 20:28:19 +0200
commit3fe777cbfd99bd6a12240273cdfc3600993775ca (patch)
tree9300ac6633891a325a0362f866ea96dc0641a38f
parentdc9ea45018bbd02df01991e94469da1ff53a291f (diff)
implemented secretKey parsing which is quite annoying. the length
has to be calculated since all length info is encrypted together with the secret key.
-rw-r--r--lib/fpg/_preload.rb4
-rw-r--r--lib/fpg/algos/_symmetric.rb7
-rw-r--r--lib/fpg/algos/asymmetric.rb7
-rw-r--r--lib/fpg/algos/cast5.rb12
-rw-r--r--lib/fpg/algos/rsa.rb9
-rw-r--r--lib/fpg/fields/key_id.rb8
-rw-r--r--lib/fpg/fields/packet_header.rb92
-rw-r--r--lib/fpg/fields/rsa.rb (renamed from lib/fpg/public_key_algos/rsa.rb)8
-rw-r--r--lib/fpg/fields/rsa_secret.rb10
-rw-r--r--lib/fpg/fields/rsa_session.rb7
-rw-r--r--lib/fpg/fields/s2k_specifier.rb12
-rw-r--r--lib/fpg/io/zappable_byte_stream.rb21
-rw-r--r--lib/fpg/packets/_packet.rb93
-rw-r--r--lib/fpg/packets/key_material.rb5
-rw-r--r--lib/fpg/packets/public_key_encrypted_session_key.rb10
-rw-r--r--lib/fpg/packets/secret_key.rb38
-rw-r--r--lib/fpg/parse/parser.rb16
-rw-r--r--lib/fpg/public_key_algos/algo.rb9
-rw-r--r--lib/fpg/public_key_algos/rsa_encryption_only.rb6
-rw-r--r--lib/fpg/public_key_algos/rsa_sign_only.rb6
-rw-r--r--spec/.public_key_spec.rb.swpbin12288 -> 0 bytes
-rw-r--r--spec/packet_parsing_spec.rb10
-rw-r--r--spec/pk_encrypted_session_key_spec.rb21
23 files changed, 279 insertions, 132 deletions
diff --git a/lib/fpg/_preload.rb b/lib/fpg/_preload.rb
index ab676ef..e8cb784 100644
--- a/lib/fpg/_preload.rb
+++ b/lib/fpg/_preload.rb
@@ -1,7 +1,3 @@
module FPG
require 'bindata'
- module Algos
- class Algo < BinData::Record ; end
- class Rsa < Algo ; end
- end
end
diff --git a/lib/fpg/algos/_symmetric.rb b/lib/fpg/algos/_symmetric.rb
new file mode 100644
index 0000000..acd9a13
--- /dev/null
+++ b/lib/fpg/algos/_symmetric.rb
@@ -0,0 +1,7 @@
+module FPG
+ module Algos
+ class Symmetric
+ include TaggedSubclasses
+ end
+ end
+end
diff --git a/lib/fpg/algos/asymmetric.rb b/lib/fpg/algos/asymmetric.rb
new file mode 100644
index 0000000..4daa794
--- /dev/null
+++ b/lib/fpg/algos/asymmetric.rb
@@ -0,0 +1,7 @@
+module FPG
+ module Algos
+ class Asymmetric
+ include TaggedSubclasses
+ end
+ end
+end
diff --git a/lib/fpg/algos/cast5.rb b/lib/fpg/algos/cast5.rb
new file mode 100644
index 0000000..d07712f
--- /dev/null
+++ b/lib/fpg/algos/cast5.rb
@@ -0,0 +1,12 @@
+module FPG
+ module Algos
+ class Cast5 < Symmetric
+ def block_size
+ 8
+ end
+ def self.tag
+ 3
+ end
+ end
+ end
+end
diff --git a/lib/fpg/algos/rsa.rb b/lib/fpg/algos/rsa.rb
new file mode 100644
index 0000000..49131d0
--- /dev/null
+++ b/lib/fpg/algos/rsa.rb
@@ -0,0 +1,9 @@
+module FPG
+ module Algos
+ class Rsa < Asymmetric
+ def self.tag
+ 1
+ end
+ end
+ end
+end
diff --git a/lib/fpg/fields/key_id.rb b/lib/fpg/fields/key_id.rb
new file mode 100644
index 0000000..66bb4a5
--- /dev/null
+++ b/lib/fpg/fields/key_id.rb
@@ -0,0 +1,8 @@
+module FPG
+ module Fields
+ class KeyId < BinData::Record
+ endian :big
+ uint64 :id
+ end
+ end
+end
diff --git a/lib/fpg/fields/packet_header.rb b/lib/fpg/fields/packet_header.rb
new file mode 100644
index 0000000..bf0dd17
--- /dev/null
+++ b/lib/fpg/fields/packet_header.rb
@@ -0,0 +1,92 @@
+module FPG
+ module Parse
+ class PacketHeader < BinData::BasePrimitive
+ def read_and_return_value(io)
+ @io = io
+ header = io.readbytes(1).unpack("C1")[0]
+ parse_id(header)
+ parse_header(header)
+ {:packet_size=>@byte_size,:packet_num=>@id,:header_size=>@header_size}
+ end
+ def sensible_default
+ 0
+ end
+ def value_to_binary_string(value)
+ #Dummy implementation to calculate the size
+ return ".." if value[:packet_size] < 192
+ return "..." if value[:packet_size] < 8383
+ return "....." if value[:packet_size] < 4294967295
+ ".."
+ end
+
+ private
+
+ def parse_header(header)
+ @byte_size = packet_size(header)
+ @byte_size += strip_partials if partial_length?
+ end
+ def parse_id(byte)
+ raise "this is not a valid packet header" unless byte>>7 == 1
+ if new_packet?(byte)
+ @id = byte-(1<<7)-(1<<6)
+ else
+ @id = (byte-(1<<7))>>2
+ end
+ end
+ def strip_partials
+ package_start = @io.raw_io.pos
+ start = package_start + @byte_size
+ partial_size = 0
+
+ while partial_length? do
+ @io.raw_io.pos= start+partial_size
+
+ size = new_packet_size
+ partial_size += size[:s]
+
+ #remove the header packets from the stream => subclasses wont have to worry abouth this
+ @io.raw_io.zap(size[:h])
+ end
+ @io.raw_io.pos= package_start
+ partial_size
+ end
+ def partial_length?
+ @partial_length
+ end
+ def packet_size(header)
+ if new_packet?(header)
+ size = new_packet_size
+ @header_size = size[:h]+1
+ size[:s]
+ else
+ tag = old_size_tag(header)
+ @header_size = [2,3,5][tag]
+ old_packet_size(tag)
+ end
+ end
+ def new_packet_size
+ @partial_length= false
+ first = @io.readbytes(1).unpack("C1")[0]
+ return { :s => first, :h => 1 } if first < 192
+ return { :s => (((first-192)<<8)+@io.readbytes(1).unpack("C1")[0]+192), :h => 2 } if first < 224
+ return { :s => ((first+@io.readbytes(3)).unpack("N")[0]), :h => 4 } if first == 255
+ @partial_length= true
+ return { :s => (1<<(first&0x1f)), :h => 1 }
+ end
+ def old_packet_size(tag)
+ return @io.readbytes(1).unpack("C1")[0] if tag == 0
+ return @io.readbytes(2).unpack("n")[0] if tag == 1
+ return @io.readbytes(4).unpack("N")[0] if tag == 2
+ #tag==3 => size is until eof
+ raise 'partial size for old packets not implemented'
+ end
+ def old_size_tag(header)
+ #size tag = two lowest bits
+ header & 3
+ end
+ def new_packet?(byte)
+ byte>>6 == 3
+ end
+ end
+ end
+end
diff --git a/lib/fpg/public_key_algos/rsa.rb b/lib/fpg/fields/rsa.rb
index 528e808..f2c7c25 100644
--- a/lib/fpg/public_key_algos/rsa.rb
+++ b/lib/fpg/fields/rsa.rb
@@ -1,12 +1,8 @@
module FPG
- module Algos
- class Rsa < Algo
+ module Fields
+ class Rsa < BinData::Record
multi_precision_integer :n
multi_precision_integer :e
-
- def self.tag
- 1
- end
end
end
end
diff --git a/lib/fpg/fields/rsa_secret.rb b/lib/fpg/fields/rsa_secret.rb
new file mode 100644
index 0000000..9ecb175
--- /dev/null
+++ b/lib/fpg/fields/rsa_secret.rb
@@ -0,0 +1,10 @@
+module FPG
+ module Fields
+ class RsaSecret < BinData::Record
+ multi_precision_integer :d
+ multi_precision_integer :p
+ multi_precision_integer :q
+ multi_precision_integer :u
+ end
+ end
+end
diff --git a/lib/fpg/fields/rsa_session.rb b/lib/fpg/fields/rsa_session.rb
new file mode 100644
index 0000000..13397a4
--- /dev/null
+++ b/lib/fpg/fields/rsa_session.rb
@@ -0,0 +1,7 @@
+module FPG
+ module Fields
+ class RsaSession < BinData::Record
+ multi_precision_integer :session_key
+ end
+ end
+end
diff --git a/lib/fpg/fields/s2k_specifier.rb b/lib/fpg/fields/s2k_specifier.rb
new file mode 100644
index 0000000..68fb0e2
--- /dev/null
+++ b/lib/fpg/fields/s2k_specifier.rb
@@ -0,0 +1,12 @@
+module FPG
+ module Fields
+ class S2kSpecifier < BinData::Record
+ endian :big
+ uint8 :id
+ uint8 :hash_algo
+ uint64 :salt, :onlyif => lambda { id > 0 }
+ uint8 :iterations, :onlyif => lambda { id > 2 }
+
+ end
+ end
+end
diff --git a/lib/fpg/io/zappable_byte_stream.rb b/lib/fpg/io/zappable_byte_stream.rb
index 73ba0b6..5358602 100644
--- a/lib/fpg/io/zappable_byte_stream.rb
+++ b/lib/fpg/io/zappable_byte_stream.rb
@@ -39,8 +39,17 @@ module FPG
io.pos= to_internal_pos(position)
end
def seek(n,wh)
- raise 'only support relative positioning' unless wh == IO::SEEK_CUR
- self.pos= @pos+n
+ case wh
+ when IO::SEEK_CUR
+ self.pos= @pos+n
+ when IO::SEEK_END
+ io.seek(n,IO::SEEK_END)
+ @pos = io.pos - zapped.size + rewind_to_next_byte
+ when IO::SEEK_SET
+ self.pos= n
+ else
+ raise "positioning not supported #{wh}"
+ end
end
private
@@ -54,6 +63,14 @@ module FPG
def skip_to_next_byte
io.readbyte while zapped[io.pos]
end
+ def rewind_to_next_byte
+ i = 0
+ while zapped[io.pos] do
+ io.seek(-1,IO::SEEK_CUR)
+ i += 1
+ end
+ i
+ end
end
end
end
diff --git a/lib/fpg/packets/_packet.rb b/lib/fpg/packets/_packet.rb
index f1b93a7..7a8b09d 100644
--- a/lib/fpg/packets/_packet.rb
+++ b/lib/fpg/packets/_packet.rb
@@ -3,92 +3,17 @@ module FPG
require 'bindata'
class Packet < BinData::Record
include TaggedSubclasses
-
endian :big
-
- attr_accessor :byte_size, :stream, :partial_length
+ packet_header :header
- def parse_header
- @byte_size = packet_size
- @byte_size += strip_partials if partial_length?
- end
- def read(stream)
- puts "parsing #{self.class}"
- @stream = stream
- parse_header
- start = @stream.pos
- puts "size is #{byte_size}"
- super(@stream)
- raise 'parsing failed. packet length does not match with parsed content' if start+byte_size != @stream.pos
- puts "done"
- end
- def skip
- stream.seek(byte_size,IO::SEEK_CUR)
- end
- def parse_content
- fail "shoul be implemented by subclass #{self.class}"
- end
-
- private
-
- def strip_partials
- package_start = @stream.pos
- start = package_start + @byte_size
- partial_size = 0
-
- @stream = ZappableByteStream.new(stream)
-
- while partial_length? do
- @stream.pos= start+partial_size
-
- size = new_packet_size
- partial_size += size[:s]
-
- #remove the header packets from the stream => subclasses wont have to worry abouth this
- @stream.zap(size[:h])
- end
- @stream.pos= package_start
- partial_size
- end
- def partial_length?
- partial_length
- end
- def packet_size
- header = stream.getbyte
- if new_packet?(header)
- new_packet_size()[:s]
- else
- tag = old_size_tag(header)
- old_packet_size(tag)
- end
- end
- def new_packet_size
- @partial_length= false
- first = stream.getbyte
- return { :s => first, :h => 1 } if first < 192
- return { :s => (((first-192)<<8)+stream.getbyte+192), :h => 2 } if first < 224
- return { :s => ((first+stream.read(3)).unpack("N")[0]), :h => 4 } if first == 255
- @partial_length= true
- return { :s => (1<<(first&0x1f)), :h => 1 }
- end
- def old_packet_size(tag)
- return stream.getbyte if tag == 0
- return stream.read(2).unpack("n")[0] if tag == 1
- return stream.read(4).unpack("N")[0] if tag == 2
- #tag==3 => size is until eof
- #TODO this seems a bit cumbersome
- current_pos = stream.pos
- stream.seek(0,IO::SEEK_END)
- size = 1+stream.pos - current_pos
- stream.pos= current_pos
- return size
- end
- def old_size_tag(header)
- #size tag = two lowest bits
- header & 3
- end
- def new_packet?(byte)
- byte>>6 == 3
+ def byte_size
+ header[:packet_size]
+ end
+ def read(io)
+ pos = io.pos
+ super
+ parse_offset = io.pos-pos-byte_size-header[:header_size]
+ raise "parse err #{self.class}: off by #{parse_offset} bytes" if parse_offset != 0
end
end
end
diff --git a/lib/fpg/packets/key_material.rb b/lib/fpg/packets/key_material.rb
index 01fd983..4ba5f23 100644
--- a/lib/fpg/packets/key_material.rb
+++ b/lib/fpg/packets/key_material.rb
@@ -2,7 +2,10 @@ module FPG
module Packets
class KeyMaterial < Packet
def algo
- Algos::Algo.with_tag(pubkey_algo)
+ Algos::Asymmetric.with_tag(pubkey_algo)
+ end
+ def self.tag
+ -1
end
end
end
diff --git a/lib/fpg/packets/public_key_encrypted_session_key.rb b/lib/fpg/packets/public_key_encrypted_session_key.rb
index 1724d4b..38f50a7 100644
--- a/lib/fpg/packets/public_key_encrypted_session_key.rb
+++ b/lib/fpg/packets/public_key_encrypted_session_key.rb
@@ -1,8 +1,12 @@
module FPG
module Packets
- class PublicKeyEncryptedSessionKey < Packet
- attr_accessor :key_id, :encryption_algo, :session_key
- string :skip_this, :read_length => :byte_size
+ class PublicKeyEncryptedSessionKey < KeyMaterial
+ uint8 :version, :check_value => lambda { value == 3 }
+ key_id :key_id
+ uint8 :pubkey_algo
+ choice :key_material, :selection => :pubkey_algo do
+ rsa_session 1
+ end
def self.tag
1
end
diff --git a/lib/fpg/packets/secret_key.rb b/lib/fpg/packets/secret_key.rb
index fa9a599..c83a67b 100644
--- a/lib/fpg/packets/secret_key.rb
+++ b/lib/fpg/packets/secret_key.rb
@@ -1,7 +1,41 @@
module FPG
module Packets
- class SecretKey < KeyMaterial
- string :skip_this, :read_length => :byte_size
+ class SecretKey < PublicKey
+ uint8 :s2k
+ uint8 :s2k_al, :onlyif => :s2k_specifier_given?
+ s2k_specifier :s2k_specifier, :onlyif => :s2k_specifier_given?
+ string :iv, :read_length => lambda { s2k_algo.block_size },
+ :onlyif => :is_encrypted?
+ choice :secret_key_material, :onlyif => :is_not_encrypted?, :selection => :pubkey_algo do
+ rsa_secret 1
+ end
+ string :encrypted_secret_key_material, :read_length => :secret_key_bytes,
+ :onlyif => :is_encrypted?
+ uint16 :checksum, :onlyif => lambda{ ! long_checksum? }
+ uint160 :lchecksum, :onlyif => :long_checksum?
+
+ def secret_key_bytes
+ header[:header_size] + byte_size - (encrypted_secret_key_material.offset) - checksum_size
+ end
+ def checksum_size
+ (long_checksum? ? 20 : 2)
+ end
+ def s2k_algo
+ @s2k_algo ||= Algos::Symmetric.with_tag( s2k_specifier_given? ? s2k_al : s2k )
+ end
+ def s2k_specifier_given?
+ s2k >= 254
+ end
+ def is_encrypted?
+ s2k > 0
+ end
+ def is_not_encrypted?
+ ! is_encrypted?
+ end
+ def long_checksum?
+ s2k == 254
+ end
+
def self.tag
5
end
diff --git a/lib/fpg/parse/parser.rb b/lib/fpg/parse/parser.rb
index 627eeb1..82d6322 100644
--- a/lib/fpg/parse/parser.rb
+++ b/lib/fpg/parse/parser.rb
@@ -1,8 +1,9 @@
module FPG
module Parse
class Parser
- def self.parse( stream )
+ def self.parse( aStream )
packets = []
+ stream = ZappableByteStream.new(aStream)
until stream.eof? do
packets << parse_packet(stream)
#rescue Exception => e
@@ -14,9 +15,16 @@ module FPG
def self.parse_packet(stream)
num = packet_number(stream.getbyte)
stream.seek(-1,IO::SEEK_CUR)
- p = Packets::Packet.with_tag(num)
- p.read(stream)
- p
+ packet = Packets::Packet.with_tag(num)
+ if packet.is_a? SecretKey then
+ puts
+ BinData::trace_reading do
+ packet.read(stream)
+ end
+ else
+ packet.read(stream)
+ end
+ packet
end
def self.packet_number(byte)
raise "this is not a valid packet header" unless byte>>7 == 1
diff --git a/lib/fpg/public_key_algos/algo.rb b/lib/fpg/public_key_algos/algo.rb
deleted file mode 100644
index ec0954b..0000000
--- a/lib/fpg/public_key_algos/algo.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-module FPG
- module Algos
- class Algo < BinData::Record
- include TaggedSubclasses
- endian :big
- attr_accessor :key_material
- end
- end
-end
diff --git a/lib/fpg/public_key_algos/rsa_encryption_only.rb b/lib/fpg/public_key_algos/rsa_encryption_only.rb
deleted file mode 100644
index fd8df6e..0000000
--- a/lib/fpg/public_key_algos/rsa_encryption_only.rb
+++ /dev/null
@@ -1,6 +0,0 @@
-module FPG
- module Algos
- class RsaEncryptionOnly < Rsa
- end
- end
-end
diff --git a/lib/fpg/public_key_algos/rsa_sign_only.rb b/lib/fpg/public_key_algos/rsa_sign_only.rb
deleted file mode 100644
index 6e3f71b..0000000
--- a/lib/fpg/public_key_algos/rsa_sign_only.rb
+++ /dev/null
@@ -1,6 +0,0 @@
-module FPG
- module Algos
- class RsaSignOnly < Rsa
- end
- end
-end
diff --git a/spec/.public_key_spec.rb.swp b/spec/.public_key_spec.rb.swp
deleted file mode 100644
index 9af39e1..0000000
--- a/spec/.public_key_spec.rb.swp
+++ /dev/null
Binary files differ
diff --git a/spec/packet_parsing_spec.rb b/spec/packet_parsing_spec.rb
index d4949fa..376de22 100644
--- a/spec/packet_parsing_spec.rb
+++ b/spec/packet_parsing_spec.rb
@@ -21,11 +21,11 @@ describe Parser do
parsed.any?{|packet| packet.is_a? PublicSubkey}.should be_true
end
it "should find all the packets in a secretkey" do
-# parsed = Parser.parse(@binary_sec)
-# parsed.any?{|packet| packet.is_a? Signature}.should be_true
-# parsed.any?{|packet| packet.is_a? UserId}.should be_true
-# parsed.any?{|packet| packet.is_a? SecretSubkey}.should be_true
-# parsed.any?{|packet| packet.is_a? SecretKey}.should be_true
+ parsed = Parser.parse(@binary_sec)
+ parsed.any?{|packet| packet.is_a? Signature}.should be_true
+ parsed.any?{|packet| packet.is_a? UserId}.should be_true
+ parsed.any?{|packet| packet.is_a? SecretSubkey}.should be_true
+ parsed.any?{|packet| packet.is_a? SecretKey}.should be_true
end
it "should find all the packets in an encrypted message" do
parsed = Parser.parse(@msg_enc)
diff --git a/spec/pk_encrypted_session_key_spec.rb b/spec/pk_encrypted_session_key_spec.rb
new file mode 100644
index 0000000..f3ebf13
--- /dev/null
+++ b/spec/pk_encrypted_session_key_spec.rb
@@ -0,0 +1,21 @@
+require File.join(File.dirname(__FILE__), %w[spec_helper])
+
+require 'stringio'
+
+include FPG::Parse
+include FPG::Packets
+include FPG::Algos
+
+describe PublicKeyEncryptedSessionKey do
+ before(:each) do
+ @msg_enc = File.open "spec/fixtures/messages/1_enc.gpg", 'rb'
+ end
+
+ it "should find all the packets in an encrypted message" do
+ parsed = Parser.parse(@msg_enc)
+ sk = parsed.select{|packet| packet.is_a? PublicKeyEncryptedSessionKey}.first
+ sk.algo.is_a?(Rsa).should be_true
+ sk.key_id.id.should == 0x8AC9A89E54DBDCF0
+ end
+end
+