@@ -537,11 +537,24 @@ def decode_all(data)
537537 objs
538538 end
539539
540+ def traverse ( der , &blk )
541+ raise LocalJumpError unless blk
542+
543+ _ , remaining = decode0 ( der , &blk )
544+
545+ unless remaining . nil? || remaining . empty?
546+ total_read = der . size - remaining . size
547+ raise ASN1Error , "Type mismatch. Total bytes read: #{ total_read } Bytes available: #{ remaining . size } Offset: #{ total_read } "
548+ end
549+
550+ nil
551+ end
552+
540553 def decode ( data )
541554 decode0 ( data ) . first
542555 end
543556
544- def decode0 ( data )
557+ def decode0 ( data , depth = 0 , offset = 0 , & block )
545558 data = data . to_der if data . respond_to? ( :to_der )
546559
547560 first_byte , length = data . unpack ( 'CC' )
@@ -582,33 +595,53 @@ def decode0(data)
582595 data [ no_id_idx + 1 ..-1 ]
583596 end
584597
598+ hlength = no_id_idx + length_bytes
599+
585600 if is_constructed
586- decode_cons ( tag_class , id , length , value , is_indefinite_length )
601+ decode_cons ( tag_class , id , hlength , length , value , is_indefinite_length , depth , offset , & block )
587602 else
588603 if is_indefinite_length
589604 raise ASN1Error , "invalid length" if PRIMITIVE_TAG_IDS . include? ( id )
590605 end
591- decode_prim ( tag_class , id , length , value )
606+ decode_prim ( tag_class , id , hlength , length , value , depth , offset , & block )
592607 end
593608 end
594609
595- def decode_cons ( tag_class , id , length , data , is_indefinite_length )
596- remaining = data [ length ..-1 ]
597- data = data [ 0 , length ]
610+ def decode_cons ( tag_class , id , hlength , length , data , is_indefinite_length , depth , offset , &block )
611+ datalen = data . size
612+
613+ if is_indefinite_length
614+ remaining = nil
615+
616+ else
617+ remaining = data [ length ..-1 ]
618+ data = data [ 0 , length ]
619+
620+ if length > datalen
621+ raise ASN1Error , "too long"
622+ end
623+ end
624+
625+ traverse0 ( depth , offset , hlength , length == 0x80 ? 0 : length , true , tag_class , id , &block ) if block
626+
627+ offset += hlength
598628
599629 objs = [ ]
600630 has_eoc = false
601- until data . empty?
602- obj , data = decode0 ( data )
631+ until data . nil? || data . empty?
632+ datalen = data . size
633+
634+ obj , data = decode0 ( data , depth + 1 , offset , &block )
635+
636+ offset += datalen
637+ offset -= data . size if data
603638
604639 case obj
605640 when EndOfContent
606641 has_eoc = true
607642
608643 break if is_indefinite_length
609644
610- # next if is_indefinite_length
611-
612645 objs << obj
613646
614647 break
@@ -646,10 +679,14 @@ def decode_cons(tag_class, id, length, data, is_indefinite_length)
646679 return obj , remaining
647680 end
648681
649- def decode_prim ( tag_class , id , length , data )
682+ def decode_prim ( tag_class , id , hlength , length , data , depth , offset , & block )
650683 remaining = data [ length ..-1 ]
651684 data = data [ 0 , length ]
652685
686+ traverse0 ( depth , offset , hlength , length , false , tag_class , id , &block ) if block
687+
688+ offset += hlength
689+
653690 obj = if tag_class == :UNIVERSAL
654691 case id
655692 when 0 # EOC
@@ -764,5 +801,26 @@ def decode_prim(tag_class, id, length, data)
764801
765802 return obj , remaining
766803 end
804+
805+ def traverse0 ( depth , offset , hlength , length , is_constructed , tag_class , id , &block )
806+ elems = [
807+ depth , offset ,
808+ hlength , length ,
809+ is_constructed ,
810+ tag_class ,
811+ id
812+ ]
813+
814+ arity = block . arity
815+ if arity == 1
816+ block . call ( elems )
817+ else
818+ if arity < elems . size
819+ elems = elems [ 0 , arity ]
820+ end
821+
822+ block . call ( *elems )
823+ end
824+ end
767825 end
768826end
0 commit comments