@@ -548,11 +548,24 @@ def decode_all(data)
548548 objs
549549 end
550550
551+ def traverse ( der , &blk )
552+ raise LocalJumpError unless blk
553+
554+ _ , remaining = decode0 ( der , &blk )
555+
556+ unless remaining . nil? || remaining . empty?
557+ total_read = der . size - remaining . size
558+ raise ASN1Error , "Type mismatch. Total bytes read: #{ total_read } Bytes available: #{ remaining . size } Offset: #{ total_read } "
559+ end
560+
561+ nil
562+ end
563+
551564 def decode ( data )
552565 decode0 ( data ) . first
553566 end
554567
555- def decode0 ( data )
568+ def decode0 ( data , depth = 0 , offset = 0 , & block )
556569 data = data . to_der if data . respond_to? ( :to_der )
557570
558571 first_byte , length = data . unpack ( 'CC' )
@@ -593,30 +606,50 @@ def decode0(data)
593606 data [ no_id_idx + 1 ..-1 ]
594607 end
595608
609+ hlength = no_id_idx + length_bytes
610+
596611 if is_constructed
597- decode_cons ( tag_class , id , length , value , is_indefinite_length )
612+ decode_cons ( tag_class , id , hlength , length , value , is_indefinite_length , depth , offset , & block )
598613 else
599- decode_prim ( tag_class , id , length , value )
614+ decode_prim ( tag_class , id , hlength , length , value , depth , offset , & block )
600615 end
601616 end
602617
603- def decode_cons ( tag_class , id , length , data , is_indefinite_length )
604- remaining = data [ length ..-1 ]
605- data = data [ 0 , length ]
618+ def decode_cons ( tag_class , id , hlength , length , data , is_indefinite_length , depth , offset , &block )
619+ datalen = data . size
620+
621+ if is_indefinite_length
622+ remaining = nil
623+
624+ else
625+ remaining = data [ length ..-1 ]
626+ data = data [ 0 , length ]
627+
628+ if length > datalen
629+ raise ASN1Error , "too long"
630+ end
631+ end
632+
633+ traverse0 ( depth , offset , hlength , length == 0x80 ? 0 : length , true , tag_class , id , &block ) if block
634+
635+ offset += hlength
606636
607637 objs = [ ]
608638 has_eoc = false
609- until data . empty?
610- obj , data = decode0 ( data )
639+ until data . nil? || data . empty?
640+ datalen = data . size
641+
642+ obj , data = decode0 ( data , depth + 1 , offset , &block )
643+
644+ offset += datalen
645+ offset -= data . size if data
611646
612647 case obj
613648 when EndOfContent
614649 has_eoc = true
615650
616651 break if is_indefinite_length
617652
618- # next if is_indefinite_length
619-
620653 objs << obj
621654
622655 break
@@ -654,10 +687,14 @@ def decode_cons(tag_class, id, length, data, is_indefinite_length)
654687 return obj , remaining
655688 end
656689
657- def decode_prim ( tag_class , id , length , data )
690+ def decode_prim ( tag_class , id , hlength , length , data , depth , offset , & block )
658691 remaining = data [ length ..-1 ]
659692 data = data [ 0 , length ]
660693
694+ traverse0 ( depth , offset , hlength , length , false , tag_class , id , &block ) if block
695+
696+ offset += hlength
697+
661698 obj = if tag_class == :UNIVERSAL
662699 case id
663700 when 0 # EOC
@@ -772,5 +809,26 @@ def decode_prim(tag_class, id, length, data)
772809
773810 return obj , remaining
774811 end
812+
813+ def traverse0 ( depth , offset , hlength , length , is_constructed , tag_class , id , &block )
814+ elems = [
815+ depth , offset ,
816+ hlength , length ,
817+ is_constructed ,
818+ tag_class ,
819+ id
820+ ]
821+
822+ arity = block . arity
823+ if arity == 1
824+ block . call ( elems )
825+ else
826+ if arity < elems . size
827+ elems = elems [ 0 , arity ]
828+ end
829+
830+ yield elems
831+ end
832+ end
775833 end
776834end
0 commit comments