Code Monkey home page Code Monkey logo

rasn1's People

Contributors

adfoster-r7 avatar lemontree55 avatar sdaubert avatar zerosteiner avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

rasn1's Issues

Feature request: Add tracing support, for debugging purposes

It would be cool to add support for outputting parsing information as the input stream is context. This functionality would have been very useful at the start when I was implementing the initial Kerberos specification

As an example - a library that does this is https://github.com/dmendel/bindata which is a declarative library for parsing binary structure which has a tracing feature:

require 'bindata'

class Polygon < BinData::Record
  endian :little
  uint8 :num_points, :value => lambda { points.length }
  array :points, :initial_length => :num_points do
    double :x
    double :y
  end
end

data =
  "\x03\x00\x00\x00\x00\x00\x00\xf0\x3f\x00\x00\x00\x00\x00\x00\x00" \
  "\x40\x00\x00\x00\x00\x00\x00\x08\x40\x00\x00\x00\x00\x00\x00\x10" \
  "\x40\x00\x00\x00\x00\x00\x00\x14\x40\x00\x00\x00\x00\x00\x00\x18" \
  "\x40".b

BinData::trace_reading($stdout) do
  puts Polygon.read(data)
end

Outputs:

obj.num_points => 3
obj.points[0].x => 1.0
obj.points[0].y => 2.0
obj.points[1].x => 3.0
obj.points[1].y => 4.0
obj.points[2].x => 5.0
obj.points[2].y => 6.0
{:num_points=>3, :points=>[{:x=>1.0, :y=>2.0}, {:x=>3.0, :y=>4.0}, {:x=>5.0, :y=>6.0}]}

The API could be similar for RASN1, and it would be useful for users that are parsing complex structures. At the minute a parsing failure results in a crash, and I manually delete fields until I get things to parse again successfully

Unexpected error when content has the same name as the sequence

Hi there,

I was just looking at using RASN1 to parse some Kerberos models from the spec, and I just wanted to document an issue I ran into where the content had the same name as the sequence:

    Int32           ::= INTEGER (-2147483648..2147483647)
                        -- signed values representable in 32 bits

   Checksum        ::= SEQUENCE {
           cksumtype       [0] Int32,
           checksum        [1] OCTET STRING
   }

Initially I implemented this as:

require 'rasn1'
require 'openssl'

checksum_input = (
  "\x30\x19\xa0\x03\x02\x01\x07\xa1\x12\x04\x10\x9e\xf0\x84\xd6\x81" \
  "\xe5\x16\x02\x32\xb1\xc3\x4e\xad\x83\x1d\x43"
).b

class CheckSum < RASN1::Model
  sequence :checksum,
           content: [
             integer(:cksumtype, explicit: 0, constructed: true),
             octet_string(:checksum, explicit: 1, constructed: true)
           ]
end

pp RASN1.parse(checksum_input)
pp OpenSSL::ASN1.decode(checksum_input)
pp CheckSum.parse(checksum_input)

But it gives an unexpected runtime error:

/Users/user/.rvm/gems/ruby-3.0.2@foo/gems/rasn1-0.10.0/lib/rasn1/types/base.rb:473:in `raise_id_error': checksum: Expected CONTEXT CONSTRUCTED 0x1 (0xa1) but get UNIVERSAL CONSTRUCTED SEQUENCE (RASN1::ASN1Error)
        from /Users/user/.rvm/gems/ruby-3.0.2@foo/gems/rasn1-0.10.0/lib/rasn1/types/base.rb:431:in `check_id'
        from /Users/user/.rvm/gems/ruby-3.0.2@foo/gems/rasn1-0.10.0/lib/rasn1/types/base.rb:204:in `parse!'
        from /Users/user/.rvm/gems/ruby-3.0.2foo/gems/rasn1-0.10.0/lib/rasn1/model.rb:327:in `parse!'
        from /Users/user/.rvm/gems/ruby-3.0.2@foo/gems/rasn1-0.10.0/lib/rasn1/model.rb:256:in `parse'
        from foo.rb:102:in `<main>'

I realized I was able to get this working by renaming the checksum octet_string in the content:

class CheckSum < RASN1::Model
  sequence :checksum,
           content: [
             integer(:cksumtype, explicit: 0, constructed: true),
-             octet_string(:checksum, explicit: 1, constructed: true)
+             octet_string(:foo, explicit: 1, constructed: true) 
           ]
end

Which was then parsed as expected:

(CheckSum) checksum SEQUENCE:
  cksumtype CONTEXT [0] EXPLICIT INTEGER: 7
  foo CONTEXT [1] EXPLICIT OCTET STRING: "\x9E\xF0\x84\xD6\x81\xE5\x16\x022\xB1\xC3N\xAD\x83\x1DC"

I wasn't sure if this is user error or not, but thought it was worth sharing

Top-level parsing / grammar independent

Coming from #1 , it would be nice to have a top-level parsing method, like Rasn1.parse(der), which just parses like OpenSSL::ASN1.parse.

The grammar-dependent variant could be done by passing the type as an optional parameter: Rasn1.parse(der, type: my_grammar_obj).

The object-dependent parse (my_grammar_obj.parse(der)) could then reuse the previous suggestion.

In this way, one could maintain parity with openssl, while enhancing it.

Add support for explicit tag for a model sub-element

In some scenario, an element in a sequence may have it own explicit tag.

For example, in Kerberos:

Ticket          ::= [APPLICATION 1] SEQUENCE { <-------- Ticket with class: :application, explicit: 1
         tkt-vno         [0] INTEGER (5),
         realm           [1] Realm,
         sname           [2] PrincipalName,
         enc-part        [3] EncryptedData -- EncTicketPart
}

KDC-REP         ::= SEQUENCE {
        pvno            [0] INTEGER (5),
        msg-type        [1] INTEGER (11 -- AS -- | 13 -- TGS --),
        padata          [2] SEQUENCE OF PA-DATA OPTIONAL
                                -- NOTE: not empty --,
        crealm          [3] Realm,
        cname           [4] PrincipalName,
        ticket          [5] Ticket, # <-------- Ticket also requires its own explicit 1, class: :application
        enc-part        [6] EncryptedData
                                -- EncASRepPart or EncTGSRepPart,
                                -- as appropriate
}
AS-REP          ::= [APPLICATION 11] KDC-REP

Example asn dump:

Application 11 (1 elem)
  SEQUENCE (6 elem)
    [0] (1 elem)
      INTEGER 5
    [1] (1 elem)
      INTEGER 11
    [3] (1 elem)
      GeneralString DEMO.LOCAL
    [4] (1 elem)
      SEQUENCE (2 elem)
        [0] (1 elem)
          INTEGER 1
        [1] (1 elem)
          SEQUENCE (1 elem)
            GeneralString juan
    [5] (1 elem) <--------------------------- The  `ticket          [5] Ticket,`
      Application 1 (1 elem) <------ The `Ticket          ::= [APPLICATION 1] SEQUENCE {`
        SEQUENCE (4 elem)
          [0] (1 elem)
            INTEGER 5
          [1] (1 elem)
            GeneralString DEMO.LOCAL
          [2] (1 elem)
            SEQUENCE (2 elem)
              [0] (1 elem)
                INTEGER 1
              [1] (1 elem)
                SEQUENCE (2 elem)
                  GeneralString krbtgt
                  GeneralString DEMO.LOCAL
          [3] (1 elem)
            SEQUENCE (3 elem)
              [0] (1 elem)
                INTEGER 23
              [1] (1 elem)
                INTEGER 2
              [2] (1 elem)
                OCTET STRING (190 byte) 55E745C36FA22847AB9C8613EB1DA898EC671C1F15466BE04AF24D3DF7E4317A4F152…
    [6] (1 elem)
      SEQUENCE (3 elem)
        [0] (1 elem)
          INTEGER 23
        [1] (1 elem)
          INTEGER 1
        [2] (1 elem)
          OCTET STRING (248 byte) 8007783616C56763833BF842668AEBE9FEBA82821F3D154F5B70195CE202D6A58753B…

RASN1 models should support this in a way like this:

wrapper(model(:name, Model), explicit: 1, constructed: 2)

so that the Model can also have its own application tag/id if need be, and still be wrapped as with its own explicit: 5 in the parent sequence.

Model#parse! do not parse content when root element is tagged 'explicit'

Exemple:

class ExplicitTaggedSeq < Model
  sequence :seq, explicit: 0, class: :application,
           content: [integer(:id), integer(:extern_id)]
end
exp = ExplicitTaggedSeq.parse("\x60\x08\x30\x06\x02\x01\x01\x02\x01\x10")
exp.root.value #=> "\x02\x01\x01\x02\x01\x10"

Here, exp.root.value should be an array containing two RASN1::Types::Integer instances.

p exp
(ExplicitTaggedSeq) seq SEQUENCE: "\x02\x01\x01\x02\x01\x10"

but should be

p exp
(ExplicitTaggedSeq) seq SEQUENCE:
  id INTEGER: 1
  extern_id INTEGER: 16

Crash when calling to_der on a model with a explicit and constructed bit_string

Replication steps:

require 'rasn1'

#    -- Encrypted part of ticket
#    EncTicketPart   ::= [APPLICATION 3] SEQUENCE {
#            flags                   [0] TicketFlags,
#            key                     [1] EncryptionKey,
#            crealm                  [2] Realm,
#            cname                   [3] PrincipalName,
#            transited               [4] TransitedEncoding,
#            authtime                [5] KerberosTime,
#            starttime               [6] KerberosTime OPTIONAL,
#            endtime                 [7] KerberosTime,
#            renew-till              [8] KerberosTime OPTIONAL,
#            caddr                   [9] HostAddresses OPTIONAL,
#            authorization-data      [10] AuthorizationData OPTIONAL
#    }
class EncTicketPart < RASN1::Model
  sequence :EncTicketPart,
           class: :application,
           explicit: 3,
           content: [
             bit_string(:flags, explicit: 0, constructed: true, bit_length: 32),
           ]
end

example = EncTicketPart.new(
  flags: "P\xA0\x00\x00",
)

pp example.to_der

Stack:

ruby test_case.rb
/Users/user/.rvm/gems/ruby-3.0.2@metasploit-framework/gems/rasn1-0.10.0/lib/rasn1/types/bit_string.rb:54:in `value_to_der': : bit length is not set (RASN1::ASN1Error)
        from /Users/user/.rvm/gems/ruby-3.0.2@metasploit-framework/gems/rasn1-0.10.0/lib/rasn1/types/base.rb:366:in `build'
        from /Users/user/.rvm/gems/ruby-3.0.2@metasploit-framework/gems/rasn1-0.10.0/lib/rasn1/types/base.rb:171:in `to_der'
        from /Users/user/.rvm/gems/ruby-3.0.2@metasploit-framework/gems/rasn1-0.10.0/lib/rasn1/types/base.rb:364:in `build'
        from /Users/user/.rvm/gems/ruby-3.0.2@metasploit-framework/gems/rasn1-0.10.0/lib/rasn1/types/base.rb:171:in `to_der'
        from /Users/user/.rvm/gems/ruby-3.0.2@metasploit-framework/gems/rasn1-0.10.0/lib/rasn1/types/sequence.rb:73:in `map'
        from /Users/user/.rvm/gems/ruby-3.0.2@metasploit-framework/gems/rasn1-0.10.0/lib/rasn1/types/sequence.rb:73:in `value_to_der'
        from /Users/user/.rvm/gems/ruby-3.0.2@metasploit-framework/gems/rasn1-0.10.0/lib/rasn1/types/base.rb:366:in `build'
        from /Users/user/.rvm/gems/ruby-3.0.2@metasploit-framework/gems/rasn1-0.10.0/lib/rasn1/types/base.rb:171:in `to_der'
        from /Users/user/.rvm/gems/ruby-3.0.2@metasploit-framework/gems/rasn1-0.10.0/lib/rasn1/types/base.rb:364:in `build'
        from /Users/user/.rvm/gems/ruby-3.0.2@metasploit-framework/gems/rasn1-0.10.0/lib/rasn1/types/base.rb:171:in `to_der'
        from /Users/user/.rvm/gems/ruby-3.0.2@metasploit-framework/gems/rasn1-0.10.0/lib/rasn1/model.rb:312:in `to_der'
        from test_case.rb:30:in `<main>'

Redefine RASN1::Types.define_type

Make RASN1::Types.define_type no more define new types in RASN1::Types module.

May be done one of these ways:

# First option
module MyModule
  # use an option (`in` here) to specify in which module the new type will be defined
  RASN1::Types.define_type('uint32', from: RASN1::Types::Integer, in: self) do |value|
    (value >= 0) && (value < 2**32)
  end

  class MyClass < RASN1::Model
    sequence :seq, content: [uint32(:uint32)]
  end
end

# Second option
module MyModule
  # automagically define the new type in current module. I am not sure this is possible
  RASN1::Types.define_type('uint32', from: RASN1::Types::Integer) do |value|
    (value >= 0) && (value < 2**32)
  end

  class MyClass < RASN1::Model
    sequence :seq, content: [uint32(:uint32)]
  end
end

Types: drop name argument

First of all, cool that you're doing this gem! I made this gem called netsnmp which is dependent on the openssl gem ASN1 parser, and due to the many inconsistencies of jruby's equivalent parser, can't be really considered platform-independent. Having a pure-ruby parser can definitely help in the jruby space.

I started creating a wrapper around this gem, and I've found a few pitfalls regarding compatibility. The first one would be:

All types have a name as first argument. According to specs, the way to create an Integer is by doing Integer.new(:int). My question is, when is this argument not :int?. I think that this could naturally be switched to Integer.new. The same for all the other primitive types (at least).

The other thing is whether it is reasonable to expect API parity with openssl's ASN1 lib. I see that all Types respond to #to_der, which is nice.

Question is whether the types name match, the parsing happens in the same way, and the types have the same base API. For instance, for instantiation base types:

OpenSSL::ASN1::OctetString.new(@value)
OpenSSL::ASN1::Integer.new(@value)
OpenSSL::ASN1::Boolean.new(@value)
OpenSSL::ASN1::Null.new(nil)

for application types:

# equivalent to the base type?
OpenSSL::ASN1::ASN1Data.new(@value, 0, :APPLICATION)
# this is how you'd build an integer of value 2
OpenSSL::ASN1::ASN1Data.new(2, 2, :UNIVERSAL)

for decoding:

OpenSSL::ASN1.decode(der)

for attributes:

i = OpenSSL::ASN1::Integer.new(2)
i.value #=> 2
i.tag #=> 2
i.tagging #=> nil
i.tag_class #=> :UNIVERSAL
i.to_der #=> "\x02\x01\x02"

That's at least for me use-cases. Some of the things I can get around to with monkey-patching, except for the first one maybe.

Mysterious bytes in strings

Hello! When I parse some ASN using this gem, my UTF8Strings has some kind of few strange bytes (4, or sometimes 2) in the beginning, for example:

ruby parses as:

(ReceiptAttribute) receipt_attribute SEQUENCE:
    type INTEGER: 12
    version INTEGER: 1
    value ANY: "\x04\x1A\x16\x182021-03-12T16:54:12+0400"

where, for example python's ContentInfo parses this value as
"2021-03-12T16:54:12+0400"

So, the portion "\x04\x1A\x16\x18" - why it's there? Every attribute has this!
Did you ever see anything like that? Would appreciate any help

I get my data from PKSC7 container from Base64 data this way:

pkcs = OpenSSL::PKCS7.new(receipt)
pkcs.verify([], OpenSSL::X509::Store.new, nil, OpenSSL::PKCS7::NOVERIFY)
payload = Receipt.parse pkcs.data

Is it possible to have nested model!

Hello!

I have such a model, that in one of the attributes of the model there is a different nested model inside. Does it make any sense? :)

Is it somehow possible to parse it with this gem? I looked everywhere, doesn't seem like.

I'd be happy to make this functionality, if that's not too hard.

Int32/UInt32 support

Hi there,

I was just looking at using RASN1 to parse some Kerberos models from the spec, and I wanted to ask if it's possible to represent the following Int32/UInt32 types cleanly:

Int32           ::= INTEGER (-2147483648..2147483647)
                    -- signed values representable in 32 bits

UInt32          ::= INTEGER (0..4294967295)
                    -- unsigned 32 bit values

EncryptedData   ::= SEQUENCE {
        etype   [0] Int32 -- EncryptionType --,
        kvno    [1] UInt32 OPTIONAL,
        cipher  [2] OCTET STRING -- ciphertext
}

For now I'm using integer(...), but was interested to know if it's possible to be more rigorous here?

If it helps - this is the model I've converted:

require 'rasn1'
require 'openssl'

class EncryptedData < RASN1::Model
  sequence :encrypted_data,
           content: [
             integer(:etype, explicit: 0, constructed: true),
             integer(:kvno, explicit: 1, constructed: true, optional: true),
             octet_string(:cipher, explicit: 2, constructed: true)
           ]
end

encrypted_data_input = (
  "\x30\x3d\xa0\x03\x02\x01\x17\xa2\x36\x04\x34\x60\xae\x53\xa5\x0b" +
    "\x56\x2e\x46\x61\xd9\xd6\x89\x98\xfc\x79\x9d\x45\x73\x7d\x0d\x8a" +
    "\x78\x84\x4d\xd7\x7c\xc6\x50\x08\x8d\xab\x22\x79\xc3\x8d\xd3\xaf" +
    "\x9f\x5e\xb7\xb8\x9b\x57\xc5\xc9\xc5\xea\x90\x89\xc3\x63\x58"
).b

puts "RASN1:"
pp RASN1.parse(encrypted_data_input)

puts "OpenSSL:"
pp OpenSSL::ASN1.decode(encrypted_data_input)

puts "EncryptedData:"
pp EncryptedData.parse(encrypted_data_input)

Any insight would be appreciated, thanks!

Recursive type support

The ldap specification supports the concept of filters, which are defined recursively:

Filter ::= CHOICE {
             and             [0] SET SIZE (1..MAX) OF filter Filter,
             or              [1] SET SIZE (1..MAX) OF filter Filter,
             not             [2] Filter,
             equalityMatch   [3] AttributeValueAssertion,
             substrings      [4] SubstringFilter,
             greaterOrEqual  [5] AttributeValueAssertion,
             lessOrEqual     [6] AttributeValueAssertion,
             present         [7] AttributeDescription,
             approxMatch     [8] AttributeValueAssertion,
             extensibleMatch [9] MatchingRuleAssertion,
             ...  }

With the following model everything works as expected:

class Filter < RASN1::Model
  choice :filter,
         content: [
           # ... rest ommitted for simplicity...
           octet_string(:present, implicit: 7)
         ]
end

input = "\x87\x0b\x6f\x62\x6a\x65\x63\x74\x63\x6c\x61\x73\x73".b
pp Filter.parse(input)

# Result:
# (Filter) filter CHOICE:
#  present CONTEXT [7] IMPLICIT OCTET STRING: "objectclass"

However, putting in the not filter causes an issue:

require 'rasn1'

class Filter < RASN1::Model
  choice :filter,
         content: [
           # ... rest ommitted for simplicity...
           wrapper(model(:not, Filter), implicit: 2),
           # ... rest ommitted for simplicity...
           octet_string(:present, implicit: 7)
         ]
end

input = "\x87\x0b\x6f\x62\x6a\x65\x63\x74\x63\x6c\x61\x73\x73".b
pp Filter.parse(input)

# error:
# /Users/user/Documents/code/rasn1/lib/rasn1/model.rb:491:in `generate_root': stack level too deep (SystemStackError)

Feature request - change name as part of inheritance

I'm not sure if root_options would be the best place for this, but here's the general scenario/use case:

require 'rasn1'

class Request < RASN1::Model
  sequence :BaseRequest,
           content: [
             integer(:msg_type, explicit: 1, constructed: true),
             octet_string(:msg, explicit: 2, constructed: true)

           ]
end

class FooRequest < Request
  root_options name: :FooRequest, # <-- override name
               class: :application,
               explicit: 10
end

class BarRequest < Request
  root_options name: :BarRequest, # <-- override name
               class: :application,
               explicit: 12
end

result = FooRequest.new(
  msg_type: 2,
  msg: 'foo_request'
)

puts result.to_h

Current output:

{:BaseRequest=>{:msg_type=>2, :msg=>"foo_request"}}

Desired output:

- {:BaseRequest=>{:msg_type=>2, :msg=>"foo_request"}}
+ {:FooRequest=>{:msg_type=>2, :msg=>"foo_request"}}

This edgecase/requirement came up as part of Kerberos - where the spec has a base KdcReq model, and specific AsReq/TgsReq models

Feature request - Add support for model(:name, Model, explicit: 1, constructed: true)

It'd be great to support passing the explicit/constructed options to the model dsl, without needing to set the options on the RASN1::Model directly, i.e.:

diff --git a/test_case.rb b/test_case.rb
index e0773e06e1..41cbe2803c 100644
--- a/test_case.rb
+++ b/test_case.rb
@@ -72,8 +72,6 @@ end
 #    }
 class EncryptionKey < RASN1::Model
   sequence :EncryptionKey,
-           explicit: 1,
-           constructed: true,
            content: [
              integer(:keytype, explicit: 0, constructed: true),
              octet_string(:keyvalue, explicit: 1, constructed: true),
@@ -100,7 +98,7 @@ class EncTicketPart < RASN1::Model
            content: [
              bit_string(:flags, explicit: 0, constructed: true, bit_length: 32),
-             model(:key, EncryptionKey)
+             model(:key, EncryptionKey, explicit: 1, constructed: true)
            ]
 end

Scenario: The Kerberos ASN1 specification defines a reusable EncryptionKey model which can be found in multiple places within the ASN1 specification, and I'd like to define the RASN1 model once:

   EncKDCRepPart   ::= SEQUENCE {
           key             [0] EncryptionKey,
           # ....
   }

   EncTicketPart   ::= [APPLICATION 3] SEQUENCE {
           # ...
           key                     [1] EncryptionKey,
           # ...
   }

   -- Unencrypted authenticator
   Authenticator   ::= [APPLICATION 2] SEQUENCE  {
           # ...
           subkey                  [6] EncryptionKey OPTIONAL,
   }

   EncAPRepPart    ::= [APPLICATION 27] SEQUENCE {
           # ...
           subkey          [2] EncryptionKey OPTIONAL,

This is the current test case:

require 'rasn1'

enc_ticket_part_input = (
  "\x63\x82\x03\x2f\x30\x82\x03\x2b\xa0\x07\x03\x05\x00\x50\xa0\x00" \
    "\x00\xa1\x1b\x30\x19\xa0\x03\x02\x01\x17\xa1\x12\x04\x10\x4b\x55" \
    "\x4a\x51\x43\x73\x45\x52\x57\x55\x64\x49\x4b\x48\x6b\x78\xa2\x0a" \
    "\x1b\x08\x44\x57\x2e\x4c\x4f\x43\x41\x4c\xa3\x17\x30\x15\xa0\x03" \
    "\x02\x01\x01\xa1\x0e\x30\x0c\x1b\x0a\x66\x61\x6b\x65\x5f\x6d\x79" \
    "\x73\x71\x6c\xa4\x0b\x30\x09\xa0\x03\x02\x01\x00\xa1\x02\x04\x00" \
    "\xa5\x11\x18\x0f\x32\x30\x32\x32\x30\x36\x32\x30\x31\x30\x33\x39" \
    "\x34\x31\x5a\xa6\x11\x18\x0f\x32\x30\x32\x32\x30\x36\x32\x30\x31" \
    "\x30\x33\x39\x34\x31\x5a\xa7\x11\x18\x0f\x32\x30\x33\x32\x30\x36" \
    "\x31\x37\x31\x30\x33\x39\x34\x31\x5a\xa8\x11\x18\x0f\x32\x30\x33" \
    "\x32\x30\x36\x31\x37\x31\x30\x33\x39\x34\x31\x5a\xaa\x82\x02\x83" \
    "\x30\x82\x02\x7f\x30\x82\x02\x7b\xa0\x03\x02\x01\x01\xa1\x82\x02" \
    "\x72\x04\x82\x02\x6e\x30\x82\x02\x6a\x30\x82\x02\x66\xa0\x04\x02" \
    "\x02\x00\x80\xa1\x82\x02\x5c\x04\x82\x02\x58\x04\x00\x00\x00\x00" \
    "\x00\x00\x00\x01\x00\x00\x00\xbc\x01\x00\x00\x48\x00\x00\x00\x00" \
    "\x00\x00\x00\x0a\x00\x00\x00\x1e\x00\x00\x00\x08\x02\x00\x00\x00" \
    "\x00\x00\x00\x06\x00\x00\x00\x14\x00\x00\x00\x28\x02\x00\x00\x00" \
    "\x00\x00\x00\x07\x00\x00\x00\x14\x00\x00\x00\x40\x02\x00\x00\x00" \
    "\x00\x00\x00\x01\x10\x08\x00\xcc\xcc\xcc\xcc\xac\x01\x00\x00\xcc" \
    "\xcc\xcc\xcc\x6c\x5c\x00\x00\x80\x94\x11\x0c\x92\x84\xd8\x01\xff" \
    "\xff\xff\xff\xff\xff\xff\x7f\xff\xff\xff\xff\xff\xff\xff\x7f\x80" \
    "\x94\x11\x0c\x92\x84\xd8\x01\x00\x00\x00\x00\x00\x00\x00\x00\xff" \
    "\xff\xff\xff\xff\xff\xff\x7f\x14\x00\x14\x00\x97\xdf\x00\x00\x00" \
    "\x00\x00\x00\x60\xe2\x00\x00\x00\x00\x00\x00\x84\x9d\x00\x00\x00" \
    "\x00\x00\x00\x8c\x3b\x00\x00\x00\x00\x00\x00\x94\xc2\x00\x00\x00" \
    "\x00\x00\x00\x7e\xc9\x00\x00\xf4\x01\x00\x00\xf4\x01\x00\x00\x01" \
    "\x02\x00\x00\x05\x00\x00\x00\x72\x34\x00\x00\x00\x00\x00\x00\x00" \
    "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \
    "\x00\x00\x00\x83\x88\x00\x00\x10\x00\x10\x00\x9c\x94\x00\x00\xa2" \
    "\xd7\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10\x02\x00\x00\x00" \
    "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \
    "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \
    "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0a" \
    "\x00\x00\x00\x00\x00\x00\x00\x0a\x00\x00\x00\x66\x00\x61\x00\x6b" \
    "\x00\x65\x00\x5f\x00\x6d\x00\x79\x00\x73\x00\x71\x00\x6c\x00\x00" \
    "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \
    "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \
    "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \
    "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x01" \
    "\x02\x00\x00\x07\x00\x00\x00\x00\x02\x00\x00\x07\x00\x00\x00\x08" \
    "\x02\x00\x00\x07\x00\x00\x00\x06\x02\x00\x00\x07\x00\x00\x00\x07" \
    "\x02\x00\x00\x07\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \
    "\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00\x08\x00\x00\x00\x44" \
    "\x00\x57\x00\x2e\x00\x4c\x00\x4f\x00\x43\x00\x41\x00\x4c\x00\x05" \
    "\x00\x00\x00\x01\x05\x00\x00\x00\x00\x00\x05\x15\x00\x00\x00\x02" \
    "\x87\x5a\x55\xfd\x66\x7b\xcc\xf7\xbc\xaf\x16\xf4\x01\x00\x00\x00" \
    "\x00\x00\x00\x80\x94\x11\x0c\x92\x84\xd8\x01\x14\x00\x66\x00\x61" \
    "\x00\x6b\x00\x65\x00\x5f\x00\x6d\x00\x79\x00\x73\x00\x71\x00\x6c" \
    "\x00\x00\x00\x76\xff\xff\xff\xe2\x58\xd7\xf5\x06\x17\x64\x9c\x77" \
    "\xc6\xff\x16\x13\x16\x5d\xe3\x00\x00\x00\x00\x76\xff\xff\xff\xaf" \
    "\x64\xea\x1d\x9f\xa0\x63\xe9\xb1\x04\xd2\x53\xc4\x82\xce\x61\x00" \
    "\x00\x00\x00"
).b

#   EncryptionKey   ::= SEQUENCE {
#            keytype         [0] Int32 -- actually encryption type --,
#            keyvalue        [1] OCTET STRING
#    }
class EncryptionKey < RASN1::Model
  sequence :EncryptionKey,
           explicit: 1,
           constructed: true,
           content: [
             integer(:keytype, explicit: 0, constructed: true),
             octet_string(:keyvalue, explicit: 1, constructed: true),
           ]
end

#    -- Encrypted part of ticket
#    EncTicketPart   ::= [APPLICATION 3] SEQUENCE {
#            flags                   [0] TicketFlags,
#            key                     [1] EncryptionKey,
#            crealm                  [2] Realm,
#            cname                   [3] PrincipalName,
#            transited               [4] TransitedEncoding,
#            authtime                [5] KerberosTime,
#            starttime               [6] KerberosTime OPTIONAL,
#            endtime                 [7] KerberosTime,
#            renew-till              [8] KerberosTime OPTIONAL,
#            caddr                   [9] HostAddresses OPTIONAL,
#            authorization-data      [10] AuthorizationData OPTIONAL
#    }
class EncTicketPart < RASN1::Model
  sequence :EncTicketPart,
           class: :application,
           explicit: 3,
           content: [
             bit_string(:flags, explicit: 0, constructed: true, bit_length: 32),
             model(:key, EncryptionKey)
           ]
end

pp EncTicketPart.parse(enc_ticket_part_input)
# (EncTicketPart) EncTicketPart APPLICATION [3] EXPLICIT SEQUENCE:
#   flags CONTEXT [0] EXPLICIT BIT STRING: "P\xA0\x00\x00" (bit length: 32)
#   (EncryptionKey) EncryptionKey CONTEXT [1] EXPLICIT SEQUENCE:
#     keytype CONTEXT [0] EXPLICIT INTEGER: 23
#     keyvalue CONTEXT [1] EXPLICIT OCTET STRING: "KUJQCsERWUdIKHkx"

Calling to_der gives different result to original input der

Test case 1 extracted from #16

replication
require 'rasn1'
require 'base64'

RASN1::Types.define_type('Uint32', from: RASN1::Types::Integer) do |value|
  (value >= 0) && (value < 2 ** 32)
end
RASN1::Types.define_type('Int32', from: RASN1::Types::Integer) do |value|
  (value >= -2 ** 31) && (value < 2 ** 31)
end
RASN1::Types.define_type('KerberosMicroseconds', from: RASN1::Types::Integer)
RASN1::Types.define_type('KerberosTime', from: RASN1::Types::GeneralizedTime)
RASN1::Types.define_type('KerberosFlags', from: RASN1::Types::BitString)
RASN1::Types.define_type('KerberosTicketFlags', from: RASN1::Types::KerberosFlags)

class KerberosString < RASN1::Types::IA5String
  # Important: Using GraphicString's id value of 27, instead of IA5String's ID of 22
  ID = 27

  # Get ASN.1 type
  # @return [String]
  def self.type
    'KerberosString'
  end
end

RASN1::Types.define_type('KerberosString', from: KerberosString)
RASN1::Types.define_type('KerberosRealm', from: RASN1::Types::KerberosString)

p RASN1::Model.singleton_methods

class PrincipalName < RASN1::Model
  sequence :PrincipalName,
           content: [
             int32(:name_type, explicit: 0, constructed: true),
             sequence_of(:name_string, RASN1::Types::KerberosString, explicit: 1, constructed: true)
           ]
end

class KrbError < RASN1::Model
  sequence :KrbError,
           class: :application,
           explicit: 30,
           content: [
             integer(:pvno, explicit: 0, constructed: true),
             integer(:msg_type, explicit: 1, constructed: true),
             kerberos_time(:ctime, explicit: 2, constructed: true, optional: true),
             kerberos_microseconds(:cusec, explicit: 3, constructed: true, optional: true),
             kerberos_time(:stime, explicit: 4, constructed: true),
             kerberos_microseconds(:susec, explicit: 5, constructed: true),
             uint32(:error_code, explicit: 6, constructed: true),
             kerberos_realm(:crealm, explicit: 7, constructed: true, optional: true),
             # vvv should not be output vvvv - asn1 spec: `cname           [8] PrincipalName OPTIONAL,`
             wrapper(model(:cname, PrincipalName), explicit: 8, constructed: true, optional: true),
             kerberos_realm(:realm, explicit: 9, constructed: true),
             wrapper(model(:sname, PrincipalName), explicit: 10, constructed: true),
             kerberos_string(:e_text, explicit: 11, constructed: true, optional: true),
             octet_string(:e_data, explicit: 12, constructed: true, optional: true),
           ]
end

valid_data =
  "\x7e\x81\x8d\x30\x81\x8a\xa0\x03\x02\x01\x05\xa1" \
  "\x03\x02\x01\x1e\xa4\x11\x18\x0f\x32\x30\x31\x34\x31\x32\x31\x34" \
  "\x32\x32\x34\x35\x32\x32\x5a\xa5\x05\x02\x03\x07\x5a\x47\xa6\x03" \
  "\x02\x01\x18\xa9\x0c\x1b\x0a\x44\x45\x4d\x4f\x2e\x4c\x4f\x43\x41" \
  "\x4c\xaa\x1f\x30\x1d\xa0\x03\x02\x01\x01\xa1\x16\x30\x14\x1b\x06" \
  "\x6b\x72\x62\x74\x67\x74\x1b\x0a\x44\x45\x4d\x4f\x2e\x4c\x4f\x43" \
  "\x41\x4c\xac\x30\x04\x2e\x30\x2c\x30\x16\xa1\x03\x02\x01\x0b\xa2" \
  "\x0f\x04\x0d\x30\x0b\x30\x09\xa0\x03\x02\x01\x17\xa1\x02\x04\x00" \
  "\x30\x12\xa1\x03\x02\x01\x13\xa2\x0b\x04\x09\x30\x07\x30\x05\xa0" \
  "\x03\x02\x01\x17".b

# expected = {
#   KrbError: {
#     pvno: 5,
#     msg_type: 30,
#     stime: DateTime.parse('2014-12-14T22:45:22+00:00'),
#     susec: 481863,
#     error_code: 24,
#     cname: {
#       name_type: nil, name_string: []
#     },
#     realm: 'DEMO.LOCAL',
#     sname: {
#       name_type: 1,
#       name_string: ['krbtgt', 'DEMO.LOCAL']
#     },
#     e_data: "0,0\x16\xA1\x03\x02\x01\v\xA2\x0F\x04\r0\v0\t\xA0\x03\x02\x01\x17\xA1\x02\x04\x000\x12\xA1\x03\x02\x01\x13\xA2\v\x04\t0\a0\x05\xA0\x03\x02\x01\x17".b
#   }
# }

krb_error = KrbError.parse(valid_data)

p krb_error
pp krb_error.to_h

$stderr.puts "input:"
$stderr.puts Base64.encode64(valid_data)
$stderr.puts "to_der:"
$stderr.puts Base64.encode64(krb_error.to_der)

if valid_data != krb_error.to_der
  raise 'failure - input der should match to_der result'
else
  puts 'success!'
end

Tests fail due to the asn1 output being different to the input:

Application 30 (1 elem)
-  SEQUENCE (8 elem)
+  SEQUENCE (9 elem) 
    [0] (1 elem)
      INTEGER 5
    [1] (1 elem)
      INTEGER 30
    [4] (1 elem)
      GeneralizedTime 2014-12-14 22:45:22 UTC
    [5] (1 elem)
      INTEGER 481863
    [6] (1 elem)
      INTEGER 24
+    [8] (1 elem)
+      SEQUENCE (2 elem)
+        [0] (1 elem)
+          INTEGER 0
+        [1] (1 elem)
+          SEQUENCE (0 elem)
    [9] (1 elem)
      GeneralString DEMO.LOCAL
    [10] (1 elem)
      SEQUENCE (2 elem)
        [0] (1 elem)
          INTEGER 1
        [1] (1 elem)
          SEQUENCE (2 elem)
            GeneralString krbtgt
            GeneralString DEMO.LOCAL
    [12] (1 elem)
      OCTET STRING (46 byte) 302C3016A10302010BA20F040D300B3009A003020117A10204003012A103020113A20B…
        SEQUENCE (2 elem)
          SEQUENCE (2 elem)
            [1] (1 elem)
              INTEGER 11
            [2] (1 elem)
              OCTET STRING (13 byte) 300B3009A003020117A1020400
                SEQUENCE (1 elem)
                  SEQUENCE (2 elem)
                    [0] (1 elem)
                      INTEGER 23
                    [1] (1 elem)
                      OCTET STRING (0 byte)
          SEQUENCE (2 elem)
            [1] (1 elem)
              INTEGER 19
            [2] (1 elem)
              OCTET STRING (9 byte) 30073005A003020117
                SEQUENCE (1 elem)
                  SEQUENCE (1 elem)
                    [0] (1 elem)
                      INTEGER 23

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.