cddlc 0.3.5 → 0.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6d01061fd86124e6e68aae0a7ef043d23c9a2211992f9421e9333c50d8a870ee
4
- data.tar.gz: 7efe567eeca90c756dac4354791583cae6da812b05eaf4074dfb953f71d09adb
3
+ metadata.gz: f2a8aaeed392227b1d7650ddc62d010a577360f06c3bd07f879dd22d6a2e3ce9
4
+ data.tar.gz: e0db7efbdb77e5cc3f770e6a586ce710414e82419127f7b08302248943f1e045
5
5
  SHA512:
6
- metadata.gz: c051e4c6c4e319e223dc8126512ffe8c7368b985735c8b15b34ac3ae5dad183f7f80f74e3a4b90f1679877472c8c5a0968f38d73994fc4e3dbb8669a39938c09
7
- data.tar.gz: 29fce82dbdfd0bf5e14184b8445d3281a648fdd3f1fb982e37020317c23b94100449d21fed9a609a2f041fdc4a035c7842e23f861b5730480f9a828963122fef
6
+ metadata.gz: 2b95180d14a2cab74e89e76a753a8519f3aa2b17abc7809ed5b9fd1bbbaf20e2dee87a6635c279b35d89cb754b2e9ac4b9051c4eb15a628ac6accb2ad5f3ccb1
7
+ data.tar.gz: 1ad97e5fc8ac2b5242dacedb9a8521b4ea376c79f54669370d7a1d1156d10ce6ca2b3d9ce2075844d5991dee8d1fa36e17fd9d5a175317f14516e92314396e89
data/bin/cddlc CHANGED
@@ -44,7 +44,7 @@ require 'ostruct'
44
44
  $options = OpenStruct.new
45
45
  begin
46
46
  op = OptionParser.new do |opts|
47
- opts.banner = "Usage: cddlc.rb [options] [-e cddl | file.cddl... | -]"
47
+ opts.banner = "Usage: cddlc [options] [-e cddl | file.cddl... | -]"
48
48
 
49
49
  opts.on("-v", "--[no-]verbose", "Run verbosely") do |v|
50
50
  $options.verbose = v
@@ -64,7 +64,23 @@ begin
64
64
  opts.on("-2", "--[no-]cddl2", "Perform some CDDL 2.0 processing") do |v|
65
65
  $options.cddl2 = v
66
66
  end
67
- opts.on("-tFMT", "--to=FMT", [:basic, :neat, :json, :yaml, :const, :enum, :cddl], "Target format") do |v|
67
+ opts.on("-c", "--cbor-validate=FILE", "Validate CBOR file against CDDL grammar") do |v|
68
+ $options.validate = v
69
+ $options.source_format = :cbor
70
+ end
71
+ opts.on("-j", "--json-validate=FILE", "Validate JSON file against CDDL grammar") do |v|
72
+ $options.validate = v
73
+ $options.source_format = :json
74
+ end
75
+ opts.on("-d", "--diag-validate=FILE", "Validate EDN file against CDDL grammar") do |v|
76
+ $options.validate = v
77
+ $options.source_format = :diag
78
+ end
79
+ opts.on("--test", "Validate embedded EDN against CDDL grammar") do |v|
80
+ $options.validate = v
81
+ $options.source_format = :embedded
82
+ end
83
+ opts.on("-tFMT", "--to=FMT", [:basic, :neat, :json, :yaml, :const, :enum, :cddl, :diag, :edn, :cbor], "Target format") do |v|
68
84
  $options.target = v
69
85
  end
70
86
  opts.on("-sRULE", "--start=RULE", String, "Start rule name") do |v|
@@ -73,6 +89,9 @@ begin
73
89
  opts.on("-eCDDL", "CDDL model on command line") do |v|
74
90
  $options.model = v
75
91
  end
92
+ opts.on("--[no-]prelude", "Add (default)/do not add prelude") do |v|
93
+ $options.prelude = v
94
+ end
76
95
  opts.on("-iIMPORT", "--import=IMPORT", String, "Import [namespace=]reference") do |v|
77
96
  $options.import ||= []
78
97
  $options.import << v
@@ -83,11 +102,13 @@ begin
83
102
  end
84
103
  end
85
104
  op.parse!
86
- rescue Exception => e
105
+ rescue StandardError => e
87
106
  warn e
88
107
  exit 1
89
108
  end
90
109
 
110
+ $options.prelude = true if $options.validate # XXX
111
+
91
112
  if ($options.expand_generics || $options.flattening) && !$options.rules
92
113
  warn "** can't expand or flatten in tree; adding -r flag"
93
114
  $options.rules = true
@@ -131,8 +152,16 @@ else
131
152
  cddl_file << ARGF.read
132
153
  end
133
154
 
155
+ if $options.validate
156
+ $options.expand_generics = true # for now
157
+ $options.rules = true
158
+ # manually select -f/--flattening if desired
159
+ end
134
160
 
135
161
  cddl = CDDL.from_cddl(cddl_file)
162
+ if $options.prelude
163
+ cddl.rules.merge! cddl.prelude.rules
164
+ end
136
165
  result = if $options.rules
137
166
  if $options.expand_generics
138
167
  require_relative "../lib/processor/cddl-expander.rb"
@@ -150,6 +179,77 @@ result = if $options.rules
150
179
 
151
180
  warn "** can't note undefined for target != cddl" if $options.note_undefined && $options.target != :cddl
152
181
 
182
+ embedded = $options.source_format == :embedded
183
+
184
+ def parse_diag(data)
185
+ require 'cbor-diag-parser'
186
+ parser = CBOR_DIAGParser.new
187
+ if result = parser.parse(data)
188
+ result.to_rb
189
+ else
190
+ warn "*** can't parse #{data}"
191
+ warn "*** #{parser.failure_reason}"
192
+ exit 1
193
+ end
194
+ end
195
+
196
+ if fn = $options.validate
197
+ require "cbor-pure"
198
+ require "validator/validate"
199
+ data = fn == "-" ? STDIN.read : File.binread(fn) unless embedded
200
+ items = case $options.source_format
201
+ in :cbor
202
+ [[CBOR.decode(data), true]]
203
+ in :json
204
+ [[JSON.load(data), true]]
205
+ in :diag # XXX move to EDN, take diag2x options
206
+ [[parse_diag(data), true]]
207
+ in :embedded
208
+ input = cddl_file.lines(chomp: true)
209
+ tests = []
210
+ input.each do |l|
211
+ case l
212
+ when /\A;;([-+:]p?)\s*(.*)\z/
213
+ disc = $1
214
+ payload = $2
215
+ if disc == ':'
216
+ if t = tests[-1]
217
+ t[1] << payload
218
+ else
219
+ warn "*** : to what? #{l}"
220
+ end
221
+ else
222
+ tests << [disc, [payload]]
223
+ end
224
+ end
225
+ end
226
+ tests.map {|disc, lines|
227
+ [parse_diag(lines.join("\n")), disc == "+"]
228
+ }
229
+ else
230
+ fail $options.source_format
231
+ end
232
+ items.each do |item, expected|
233
+ warn [:ITEM, item, expected].inspect if CDDL::CDDLC_DEBUG
234
+ r = cddl.validate(item)
235
+ if r[0]
236
+ # TODO: build annotated output here!
237
+ warn r.inspect
238
+ if !expected
239
+ warn "*** should NOT have validated"
240
+ exit 1
241
+ end
242
+ else
243
+ puts r.to_yaml
244
+ if expected
245
+ warn "*** should have validated" if embedded
246
+ exit 1
247
+ end
248
+ end
249
+ end
250
+ exit
251
+ end
252
+
153
253
  case $options.target
154
254
  when :basic, nil
155
255
  pp result
@@ -189,5 +289,5 @@ when :cddl
189
289
  end
190
290
  puts cddl.to_s
191
291
  else
192
- warn ["Unknown target format: ", $options.target].inspect
292
+ warn ["Unknown target format for CDDL output: ", $options.target].inspect
193
293
  end
data/cddlc.gemspec CHANGED
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = "cddlc"
3
- s.version = "0.3.5"
3
+ s.version = "0.4.2"
4
4
  s.summary = "CDDL (Concise Data Definition Language) converters and miscellaneous tools"
5
5
  s.description = %q{cddlc implements converters and miscellaneous tools for CDDL, RFC 8610}
6
6
  s.author = "Carsten Bormann"
data/data/rfc9360.cddl ADDED
@@ -0,0 +1,2 @@
1
+ COSE_X509 = bstr / [ 2*certs: bstr ]
2
+ COSE_CertHash = [ hashAlg: (int / tstr), hashValue: bstr ]
data/data/rfc9431.cddl ADDED
@@ -0,0 +1,5 @@
1
+ AIF-MQTT = AIF-Generic<mqtt-topic-filter, mqtt-permissions>
2
+ AIF-Generic<Toid, Tperm> = [* [Toid, Tperm]]
3
+ mqtt-topic-filter = tstr ; as per Section 4.7 of MQTT v5.0
4
+ mqtt-permissions = [+permission]
5
+ permission = "pub"/"sub"
data/data/rfc9526.cddl ADDED
@@ -0,0 +1,12 @@
1
+ hna-configuration = {
2
+ "registered_domain" : tstr,
3
+ "dm" : tstr,
4
+ ? "dm_transport" : "DoT"
5
+ ? "dm_port" : uint,
6
+ ? "dm_acl" : hna-acl / [ +hna-acl ]
7
+ ? "hna_auth_method": hna-auth-method
8
+ ? "hna_certificate": tstr
9
+ }
10
+
11
+ hna-acl = tstr
12
+ hna-auth-method /= "certificate"
data/data/rfc9528.cddl ADDED
@@ -0,0 +1,59 @@
1
+ suites = [ 2* int ] / int
2
+
3
+ ead = (
4
+ ead_label : int,
5
+ ? ead_value : bstr,
6
+ )
7
+
8
+ EAD_1 = (1* ead)
9
+ EAD_2 = (1* ead)
10
+ EAD_3 = (1* ead)
11
+ EAD_4 = (1* ead)
12
+
13
+ message_1 = (
14
+ METHOD : int,
15
+ SUITES_I : suites,
16
+ G_X : bstr,
17
+ C_I : bstr / -24..23,
18
+ ? EAD_1,
19
+ )
20
+
21
+ message_2 = (
22
+ G_Y_CIPHERTEXT_2 : bstr,
23
+ )
24
+
25
+ PLAINTEXT_2 = (
26
+ C_R : bstr / -24..23,
27
+ ID_CRED_R : header_map / bstr / -24..23,
28
+ Signature_or_MAC_2 : bstr,
29
+ ? EAD_2,
30
+ )
31
+
32
+ message_3 = (
33
+ CIPHERTEXT_3 : bstr,
34
+ )
35
+
36
+ PLAINTEXT_3 = (
37
+ ID_CRED_I : header_map / bstr / -24..23,
38
+ Signature_or_MAC_3 : bstr,
39
+ ? EAD_3,
40
+ )
41
+
42
+ message_4 = (
43
+ CIPHERTEXT_4 : bstr,
44
+ )
45
+
46
+ PLAINTEXT_4 = (
47
+ ? EAD_4,
48
+ )
49
+
50
+ error = (
51
+ ERR_CODE : int,
52
+ ERR_INFO : any,
53
+ )
54
+
55
+ info = (
56
+ info_label : int,
57
+ context : bstr,
58
+ length : uint,
59
+ )
@@ -0,0 +1,3 @@
1
+ ace-groupcomm-error = {
2
+ &(error-id: 0) => int
3
+ }
@@ -0,0 +1,20 @@
1
+ ;# include rfc9237
2
+
3
+ gname = tstr
4
+
5
+ permissions = uint .bits roles
6
+
7
+ roles = &(
8
+ Requester: 1,
9
+ Responder: 2,
10
+ Monitor: 3,
11
+ Verifier: 4
12
+ )
13
+
14
+ scope_entries = AIF-Generic<gname, permissions>
15
+
16
+ scope = bstr .cbor scope_entries
17
+
18
+ extended_scope = #6.<TAG_FOR_THIS_SEMANTICS>(scope)
19
+
20
+ TAG_FOR_THIS_SEMANTICS = uint
@@ -0,0 +1,13 @@
1
+ gname = tstr
2
+
3
+ role = tstr
4
+
5
+ scope_entry = [ gname , ? ( role / [ 2*role ] ) ]
6
+
7
+ scope_entries = [ * scope_entry ]
8
+
9
+ scope = bstr .cbor scope_entries
10
+
11
+ extended_scope = #6.<TAG_FOR_THIS_SEMANTICS>(scope)
12
+
13
+ TAG_FOR_THIS_SEMANTICS = uint
@@ -0,0 +1,16 @@
1
+ ;# include rfc9237
2
+
3
+ gname = tstr
4
+
5
+ permissions = uint .bits roles
6
+
7
+ roles = &(
8
+ Requester: 1,
9
+ Responder: 2,
10
+ Monitor: 3,
11
+ Verifier: 4
12
+ )
13
+
14
+ scope_entries = AIF-Generic<gname, permissions>
15
+
16
+ scope = bstr .cbor scope_entries
@@ -0,0 +1,9 @@
1
+ gname = tstr
2
+
3
+ role = tstr
4
+
5
+ scope_entry = [gname, ? ( role / [2* role] )]
6
+
7
+ scope_entries = [* scope_entry]
8
+
9
+ scope = bstr .cbor scope_entries
@@ -0,0 +1,10 @@
1
+ inclusion_flag = bool
2
+
3
+ role = tstr
4
+ comb_role = [2* role]
5
+ role_filter = [* ( role / comb_role )]
6
+
7
+ id = bstr
8
+ id_filter = [* id]
9
+
10
+ get_creds = null / [inclusion_flag, role_filter, id_filter]
@@ -0,0 +1,18 @@
1
+ sign_info = sign_info_req / sign_info_resp
2
+
3
+ sign_info_req = null ; in the Token Transfer
4
+ ; Request to the KDC
5
+
6
+ sign_info_resp = [+ sign_info_entry] ; in the Token Transfer
7
+ ; Response from the KDC
8
+
9
+ sign_info_entry =
10
+ [
11
+ id: gname / [+ gname],
12
+ sign_alg: int / tstr,
13
+ sign_parameters: [any],
14
+ sign_key_parameters: [+ parameter: any],
15
+ cred_fmt: int / null
16
+ ]
17
+
18
+ gname = tstr
@@ -0,0 +1,10 @@
1
+ sign_info_entry =
2
+ [
3
+ id : gname / [+ gname],
4
+ sign_alg : int / tstr,
5
+ sign_parameters : [* alg_capab : any],
6
+ * sign_capab : [* capab : any],
7
+ cred_fmt : int / null
8
+ ]
9
+
10
+ gname = tstr
data/data/rfc9597.cddl ADDED
@@ -0,0 +1,5 @@
1
+ CWT-Claims = {
2
+ * Claim-Label => any
3
+ }
4
+
5
+ Claim-Label = int / text
data/lib/cddlc.rb CHANGED
@@ -321,4 +321,51 @@ class CDDL
321
321
  end
322
322
  @prelude
323
323
  end
324
+
325
+ # In each specific case, use the appropriate subset
326
+ ESCAPED_STRING_MAPPINGS = Hash[[
327
+ ["\x08", "b"],
328
+ ["\x09", "t"],
329
+ ["\x0A", "n"],
330
+ ["\x0C", "f"],
331
+ ["\x0D", "r"],
332
+ ["\x22", "\""],
333
+ ["\x27", "'"],
334
+ ["\x2F", "/"],
335
+ ["\x5C", "\\"],
336
+ ]]
337
+
338
+ # TODO: Enable selecting Unicode-friendly variants
339
+ def escape_string(s, regexp = /[^\n !#-\[\]-~]/)
340
+ s.gsub(regexp) {|ch|
341
+ if m = ESCAPED_STRING_MAPPINGS[ch]
342
+ "\\#{m}"
343
+ elsif (o = ch.ord) < 0x10000
344
+ "\\u#{"%04x" % o}"
345
+ else
346
+ "\\u{#{"%x" % o}}"
347
+ end
348
+ }
349
+ end
350
+
351
+ def escape_byte_string(s)
352
+ escape_string(s, /[^\n -&(-\[\]-~]/)
353
+ end
354
+
355
+ def bytes_escaped(tesc, t)
356
+ if tesc.nil?
357
+ case t
358
+ in ["", text]
359
+ "'" + escape_byte_string(text)
360
+ in [/\Ah\z/i, text]
361
+ "h'" + escape_byte_string(text)
362
+ in [/\Ab64\z/i, text]
363
+ "b64'" + escape_byte_string(text)
364
+ # else error
365
+ end + "'"
366
+ else
367
+ tesc
368
+ end
369
+ end
370
+
324
371
  end
@@ -4166,7 +4166,9 @@ module CDDLGRAMMAR
4166
4166
  end
4167
4167
 
4168
4168
  module Bytes1
4169
- def ast() ["bytes", text_value] end
4169
+ def ast()
4170
+ ["bytes", [elements[0].text_value, elements[2].elements.map(&:ast).join], text_value]
4171
+ end
4170
4172
  end
4171
4173
 
4172
4174
  def _nt_bytes
@@ -4235,6 +4237,30 @@ module CDDLGRAMMAR
4235
4237
  r0
4236
4238
  end
4237
4239
 
4240
+ module BCHAR0
4241
+ def ast() text_value end
4242
+ end
4243
+
4244
+ module BCHAR1
4245
+ def ast() text_value end
4246
+ end
4247
+
4248
+ module BCHAR2
4249
+ def ast() text_value end
4250
+ end
4251
+
4252
+ module BCHAR3
4253
+ def ast() text_value end
4254
+ end
4255
+
4256
+ module BCHAR4
4257
+ def ast() "'" end
4258
+ end
4259
+
4260
+ module BCHAR5
4261
+ def ast() "\n" end
4262
+ end
4263
+
4238
4264
  def _nt_BCHAR
4239
4265
  start_index = index
4240
4266
  if node_cache[:BCHAR].has_key?(index)
@@ -4248,7 +4274,9 @@ module CDDLGRAMMAR
4248
4274
 
4249
4275
  i0 = index
4250
4276
  if has_terminal?(@regexps[gr = '\A[\\ -&]'] ||= Regexp.new(gr), :regexp, index)
4251
- r1 = true
4277
+ r1 = instantiate_node(SyntaxNode,input, index...(index + 1))
4278
+ r1.extend(BCHAR0)
4279
+ r1.extend(BCHAR0)
4252
4280
  @index += 1
4253
4281
  else
4254
4282
  terminal_parse_failure('[\\ -&]')
@@ -4259,7 +4287,9 @@ module CDDLGRAMMAR
4259
4287
  r0 = r1
4260
4288
  else
4261
4289
  if has_terminal?(@regexps[gr = '\A[\\(-\\[]'] ||= Regexp.new(gr), :regexp, index)
4262
- r2 = true
4290
+ r2 = instantiate_node(SyntaxNode,input, index...(index + 1))
4291
+ r2.extend(BCHAR1)
4292
+ r2.extend(BCHAR1)
4263
4293
  @index += 1
4264
4294
  else
4265
4295
  terminal_parse_failure('[\\(-\\[]')
@@ -4270,7 +4300,9 @@ module CDDLGRAMMAR
4270
4300
  r0 = r2
4271
4301
  else
4272
4302
  if has_terminal?(@regexps[gr = '\A[\\]-~]'] ||= Regexp.new(gr), :regexp, index)
4273
- r3 = true
4303
+ r3 = instantiate_node(SyntaxNode,input, index...(index + 1))
4304
+ r3.extend(BCHAR2)
4305
+ r3.extend(BCHAR2)
4274
4306
  @index += 1
4275
4307
  else
4276
4308
  terminal_parse_failure('[\\]-~]')
@@ -4281,6 +4313,8 @@ module CDDLGRAMMAR
4281
4313
  r0 = r3
4282
4314
  else
4283
4315
  r4 = _nt_NONASCII
4316
+ r4.extend(BCHAR3)
4317
+ r4.extend(BCHAR3)
4284
4318
  if r4
4285
4319
  r4 = SyntaxNode.new(input, (index-1)...index) if r4 == true
4286
4320
  r0 = r4
@@ -4292,6 +4326,7 @@ module CDDLGRAMMAR
4292
4326
  else
4293
4327
  if (match_len = has_terminal?("\\'", false, index))
4294
4328
  r6 = instantiate_node(SyntaxNode,input, index...(index + match_len))
4329
+ r6.extend(BCHAR4)
4295
4330
  @index += match_len
4296
4331
  else
4297
4332
  terminal_parse_failure('"\\\\\'"')
@@ -4302,6 +4337,8 @@ module CDDLGRAMMAR
4302
4337
  r0 = r6
4303
4338
  else
4304
4339
  r7 = _nt_CRLF
4340
+ r7.extend(BCHAR5)
4341
+ r7.extend(BCHAR5)
4305
4342
  if r7
4306
4343
  r7 = SyntaxNode.new(input, (index-1)...index) if r7 == true
4307
4344
  r0 = r7
@@ -5148,9 +5185,11 @@ module CDDLGRAMMAR
5148
5185
  r0
5149
5186
  end
5150
5187
 
5151
- end
5152
5188
 
5153
- class CDDLGRAMMARParser < Treetop::Runtime::CompiledParser
5154
- include CDDLGRAMMAR
5189
+ class Parser < Treetop::Runtime::CompiledParser
5190
+ include CDDLGRAMMAR
5191
+ end
5155
5192
  end
5156
5193
 
5194
+ CDDLGRAMMARParser = CDDLGRAMMAR::Parser
5195
+
@@ -0,0 +1,489 @@
1
+ # requires for CBOR
2
+
3
+ require 'cbor-pure' unless defined?(CBOR::Tagged)
4
+ require 'half'
5
+
6
+ # requires for control operators
7
+ require 'cbor-deterministic'
8
+ require 'regexp-examples'
9
+ require 'abnftt'
10
+ require 'base64'
11
+ require 'base32'
12
+ require 'base45_lite'
13
+ require 'scanf'
14
+
15
+ # Hmm:
16
+ #!/usr/bin/env RUBY_THREAD_VM_STACK_SIZE=5000000 ruby
17
+
18
+
19
+ class CDDL
20
+
21
+ # DATA_DIR = Pathname.new(__FILE__).split[0] + '../../data'
22
+ # PRELUDE = File.read("#{DATA_DIR}/prelude.cddl") -> #prelude -- parsed
23
+
24
+ MANY = Float::INFINITY
25
+
26
+ MAX_RECURSE = 128 # XXX
27
+
28
+ CDDLC_INVENT = ENV["CDDLC_INVENT"]
29
+ CDDLC_DEBUG = ENV["CDDLC_DEBUG"]
30
+
31
+ FEATURE_REJECT_RE = /\A\^/
32
+ # CDDLC_FEATURE_OK=cbor,^json
33
+ CDDLC_FEATURE_OK, CDDLC_FEATURE_REJECT =
34
+ if ok = ENV["CDDLC_FEATURE_OK"]
35
+ ok.split(/,\s*/)
36
+ .partition{|s| s[0] !~ FEATURE_REJECT_RE}
37
+ .map {|l| Hash[l.map {|feature|
38
+ [feature.sub(FEATURE_REJECT_RE, ''),
39
+ true]}]}
40
+ else
41
+ [{}, {}]
42
+ end
43
+
44
+ REGEXP_FOR_STRING = Hash.new {|h, k|
45
+ h[k] = Regexp.new("\\A(?:#{k})\\z")
46
+ }
47
+
48
+ ABNF_PARSER_FOR_STRING = Hash.new {|h, k|
49
+ grammar = "cddl-t0p--1eve1-f0r--abnf = " << k # XXX
50
+ h[k] = ABNF.from_abnf(grammar)
51
+ }
52
+
53
+ ABNF_ENCODING_FOR_CONOP = {
54
+ ".abnf" => Encoding::UTF_8,
55
+ ".abnfb" => Encoding::BINARY
56
+ }
57
+
58
+
59
+ # library:
60
+
61
+ def remove_indentation(s)
62
+ l = s.lines
63
+ indent = l.grep(/\S/).map {|l| l[/^\s*/].size}.min
64
+ l.map {|l| l.sub(/^ {0,#{indent}}/, "")}.join
65
+ end
66
+
67
+ # [success, item, info, sublist (a/m/t)]
68
+ # [true, item, annos, sub-results [kv]]
69
+ # [false, item, error, sub-results [kv]]
70
+ # separate bad specs (always fails) from non-matching specs
71
+
72
+ def validate(item)
73
+ @rootrule ||= @rules.keys.first
74
+ # boxify item
75
+ validate1(item, ["name", @rootrule])
76
+ end
77
+
78
+ def numval(s)
79
+ Integer(s) rescue Float(s)
80
+ end
81
+
82
+ def validate1(item, where)
83
+ pp [:VALIDATE1, item, where].inspect if CDDLC_DEBUG
84
+ anno = nil
85
+ case where
86
+ in ["name", name]
87
+ # invent or error out if !rules[name]
88
+ rhs = rules[name]
89
+ unless rhs
90
+ if s = CDDLC_INVENT
91
+ s = "_" if s == ""
92
+ rules[name] = rhs = ["text", "#{s}-#{name}"]
93
+ else
94
+ return [false, item, {undefined: [name]}, []]
95
+ end
96
+ end
97
+ r = validate1(item, rhs)
98
+ case r
99
+ in [true, item, _annos, _sub]
100
+ [true, item, {name: name}, [r]]
101
+ in [false, item, error, _sub]
102
+ [false, item, {nomatch: [name, error]}, [r]]
103
+ else
104
+ fail [:MALFORMED_R, r, item, rhs].inspect
105
+ end
106
+ in ["number", num]
107
+ val = numval(num)
108
+ if !(item.eql? val) # TODO: 0.0 vs. -0.0
109
+ [false, item, {wrongnumber: [item, val, num]}, []]
110
+ else
111
+ [true, item, {}, []]
112
+ end
113
+ in ["text", val]
114
+ if !(item.eql? val) # check text vs. bytes
115
+ [false, item, {wrongtext: [item, val]}, []]
116
+ else
117
+ [true, item, {}, []]
118
+ end
119
+ in ["bytes", val]
120
+ # XXX Need to fix abnftt to yield correct value
121
+ if !(item.eql? val) # check text vs. bytes
122
+ [false, item, {wrongtext: [item, val]}, []]
123
+ else
124
+ [true, item, {}, []]
125
+ end
126
+ in ["tcho", *choices]
127
+ nomatches = []
128
+ choices.each do |where|
129
+ r = validate1(item, where)
130
+ if r[0]
131
+ pp [:TCHO, item, where, r] if CDDLC_DEBUG
132
+ return r
133
+ end
134
+ nomatches << r
135
+ end
136
+ [false, item, nomatches, [r]]
137
+ in ["prim"]
138
+ [true, item, {}, []]
139
+ in ["prim", 0]
140
+ simple_result(Integer === item && item >= 0 && item <= 0xffffffffffffffff,
141
+ item, where, :wrongnumber)
142
+ in ["prim", 1]
143
+ simple_result(Integer === item && item < 0 && item >= -0x10000000000000000,
144
+ item, where, :wrongnumber)
145
+ in ["prim", 2]
146
+ simple_result(String === item && item.encoding == Encoding::BINARY,
147
+ item, where, :wrongbytes)
148
+ in ["prim", 3]
149
+ simple_result(String === item && item.encoding != Encoding::BINARY, # cheat
150
+ item, where, :wrongtext)
151
+ in ["prim", 6, *wh2]
152
+ warn [:WH2, wh2].inspect
153
+ d = if Integer === item
154
+ biggify(item)
155
+ else
156
+ item
157
+ end
158
+ # XXX validate tag against headnum if present
159
+ if CBOR::Tagged === d
160
+ r0 = if Integer === wh2[0]
161
+ simple_result(d.tag == wh2[0], d.tag, wh2[0], :wrongtag)
162
+ else validate1(d.tag, wh2[0])
163
+ end
164
+ r1 = validate1(d.data, wh2[1])
165
+ if r0[0] && r1[0]
166
+ [true, item, {}, []] # XXX add diagnosics
167
+ else
168
+ [false, item, {wrongtag: [item, where]}, [r0, r1]]
169
+ end
170
+ else
171
+ [false, item, {not_a_tag: [item, where]}, []]
172
+ end
173
+ in ["prim", 7, *ai]
174
+ # t, v = extract_value(where) -- if t -- v.eql? d --
175
+ headnum = case item
176
+ when Float
177
+ FLOAT_AI_FROM_SIZE[item.to_cbor.size]
178
+ when CBOR::Simple
179
+ item.value
180
+ when false
181
+ 20
182
+ when true
183
+ 21
184
+ when nil
185
+ 22
186
+ end
187
+ if Array === ai[0] # CDDL for head number
188
+ validate1(headnum, ai[0])
189
+ else
190
+ simple_result(
191
+ ai[0].nil? ||
192
+ ai[0] == headnum,
193
+ item, where, :wrong7)
194
+ end
195
+ in ["op", op, lhs, rhs]
196
+ case op
197
+ in ".." | "..."
198
+ rex = RANGE_EXCLUDE_END[op]
199
+ lhss, lhsv, lhst = extract_value(lhs)
200
+ rhss, rhsv, rhst = extract_value(rhs)
201
+ if !lhss || !rhss
202
+ [false, item, {UNSPECIFIC_RANGE: [op, lhs, rhs]}, []]
203
+ elsif lhst != rhst
204
+ [false, item, {INCOHERENT_RANGE: [op, lhs, rhs]}, []]
205
+ else
206
+ st = scalar_type(item)
207
+ if lhst != st
208
+ [false, item, {rangetype: [op, lhs, rhs]}, []]
209
+ else
210
+ rg = Range.new(lhsv, rhsv, rex)
211
+ simple_result(
212
+ rg.include?(item),
213
+ item, where, :out_of_range)
214
+ end
215
+ end
216
+ in ".cat" | ".det" | ".plus"
217
+ s, v, t = extract_value(where)
218
+ pp [:CAT_DET_PLUS, s, v, t, item, scalar_type(item)] if CDDLC_DEBUG
219
+ if s
220
+ simple_result(s && scalar_type(item) == t && item == v,
221
+ item, where, :no_match)
222
+ else
223
+ [false, item, v, []]
224
+ end
225
+ in ".size"
226
+ anno = :lhs
227
+ r = validate1(item, lhs)
228
+ if r[0]
229
+ case item
230
+ when Integer
231
+ ok, v, vt = extract_value(rhs)
232
+ if ok && vt == :int
233
+ simple_result((item >> (8*v)) == 0,
234
+ item, where, :toolarge)
235
+ end
236
+ when String
237
+ validate1(item.bytesize, rhs)
238
+ else
239
+ false
240
+ end
241
+ end
242
+ in ".bits"
243
+ anno = :lhs
244
+ r = validate1(item, lhs)
245
+ if r[0]
246
+ if String === item
247
+ simple_result(
248
+ item.each_byte.with_index.all? { |b, i|
249
+ bit = i << 3
250
+ 8.times.all? { |nb|
251
+ b[nb] == 0 || validate1(bit+nb, rhs)[0] # collect
252
+ }
253
+ },
254
+ item, where, :unwanted_bit_set)
255
+ elsif Integer === item
256
+ if item >= 0
257
+ ok = true
258
+ i = 0
259
+ d = item
260
+ while ok && d > 0
261
+ if d.odd?
262
+ ok &&= validate1(i, rhs)[0] # collect
263
+ end
264
+ d >>= 1; i += 1
265
+ end
266
+ simple_result(ok,
267
+ item, where, :unwanted_bit_set)
268
+ end
269
+ end
270
+ end
271
+ in ".default"
272
+ # anno = :lhs
273
+ r = validate1(item, lhs)
274
+ # TO DO
275
+ unless @default_warned
276
+ warn "*** Ignoring .default for now."
277
+ @default_warned = true
278
+ end
279
+ r
280
+ in ".feature"
281
+ r = validate1(item, lhs)
282
+ if r[0]
283
+ nm, det = extract_feature(rhs, d)
284
+ if CDDLC_FEATURE_REJECT[nm]
285
+ [false, item, {:rejected_feature => [nm, det]}, []]
286
+ else
287
+ [true, item, {:accepted_feature => [nm, det]}, []]
288
+ end
289
+ end
290
+
291
+ in ".regexp"
292
+ anno = :lhs
293
+ r = validate1(item, lhs)
294
+ if r[0]
295
+ if String === item
296
+ ok, v, vt = extract_value(rhs)
297
+ if ok && :text == vt
298
+ re = REGEXP_FOR_STRING[v]
299
+ # pp re if CDDLC_DEBUG
300
+ simple_result(item.match(re),
301
+ item, where, :regexp_not_matched)
302
+ end
303
+ end
304
+ end
305
+ in ".abnf" | ".abnfb"
306
+ anno = :lhs
307
+ r = validate1(item, lhs)
308
+ if r[0]
309
+ if String === item
310
+ ok, v, vt = extract_value(rhs)
311
+ # pp [:abnfex, rhs, ok, v, vt] if CDDLC_DEBUG
312
+ if ok && (:text == vt || :bytes == vt)
313
+ begin
314
+ ABNF_PARSER_FOR_STRING[v].validate(
315
+ item.dup.force_encoding(ABNF_ENCODING_FOR_CONOP[op]).codepoints.pack("U*"))
316
+ [true, item, {abnf: [v]}, [r]]
317
+ rescue => e
318
+ # warn "*** #{e}" # XXX
319
+ [false, item, {abnf_not_matched: [v, e.to_s.force_encoding(Encoding::UTF_8)] }, [r]]
320
+ end
321
+ end
322
+ end
323
+ end
324
+
325
+ else
326
+ fail [:CONTROL_UNIMPLEMENTED, op, item, where].inspect
327
+ end
328
+ else
329
+ warn [:UNIMPLEMENTED, item, where].inspect
330
+ exit 1
331
+ end || [false, item, {anno => [item, where]}, []]
332
+ end
333
+
334
+ def scalar_type(item)
335
+ case item
336
+ in NilClass
337
+ :null
338
+ in FalseClass | TrueClass
339
+ :bool
340
+ in Integer
341
+ :int
342
+ in Float
343
+ :float
344
+ in String if item.encoding == Encoding::BINARY
345
+ :bytes
346
+ in String
347
+ :text
348
+ else
349
+ nil
350
+ end
351
+ end
352
+
353
+ FLOAT_AI_FROM_SIZE = {3 => 25, 5 => 26, 9 => 27}
354
+ SIMPLE_VALUE = {
355
+ [:prim, 7, 20] => [true, false, :bool],
356
+ [:prim, 7, 21] => [true, true, :bool],
357
+ [:prim, 7, 22] => [true, nil, :nil],
358
+ }
359
+ SIMPLE_VALUE_SIMPLE = Set[23] + (0..19) + (32..255)
360
+ RANGE_EXCLUDE_END = {".." => false, "..." => true}
361
+
362
+ def simple_result(check, item, where, anno)
363
+ if check
364
+ [true, item, {}, []]
365
+ else
366
+ [false, item, {anno => [item, where]}, []]
367
+ end
368
+ end
369
+
370
+ def biggify(d) # stand-in for real stand-ins
371
+ t = 2 # assuming only 2/3 match an Integer
372
+ if d < 0
373
+ d = ~d
374
+ t = 3
375
+ end
376
+ CBOR::Tagged.new(t, d == 0 ? "".b : d.digits(256).reverse!.pack("C*"))
377
+ end
378
+
379
+ def extract_bytes(bsqual, bsval)
380
+ if bsqual == ""
381
+ bsval.b
382
+ else
383
+ bsclean = bsval.gsub(/\s/, "")
384
+ case bsqual
385
+ in /\Ah\z/i
386
+ bsclean.chars.each_slice(2).map{ |x| Integer(x.join, 16).chr("BINARY") }.join.b
387
+ in /\Ab64\z/i
388
+ begin
389
+ Base64.urlsafe_decode64(bsclean)
390
+ rescue ArgumentError => e
391
+ {base64_error: [bsclean, e.to_s]}
392
+ end
393
+ else
394
+ warn "*** Can't handle byte string type #{bsqual.inspect} yet"
395
+ end
396
+ end
397
+ end
398
+
399
+ def extract_value(wh)
400
+ case wh
401
+ in x if a = SIMPLE_VALUE[x]
402
+ SIMPLE_VALUE[x]
403
+ in ["number", num]
404
+ [true, Integer(num), :int] rescue [true, Float(num), :float]
405
+ in ["text", val]
406
+ [true, val, :text]
407
+ in ["bytes", val, _orig]
408
+ eb = extract_bytes(*val)
409
+ if String === eb
410
+ [true, extract_bytes(*val), :bytes]
411
+ else
412
+ [false, eb]
413
+ end
414
+ in ["op", ".cat" | ".det" | ".plus", lhs, rhs]
415
+ op = wh[1]
416
+ expected_type = op == ".plus" ? Numeric : String;
417
+ lhss, lhsv, lhst = extract_value(lhs)
418
+ rhss, rhsv, rhst = extract_value(rhs)
419
+ if !lhss || !rhss
420
+ [false, {%i'UNSPECIFIC_#{op}' => [op, lhs, rhs]}]
421
+ elsif [lhsv, rhsv].any? {!(expected_type === _1)}
422
+ [false, {%i'BAD_TYPES_#{op}' => [op, lhs, rhs]}]
423
+ else
424
+ if op == ".det"
425
+ lhsv = remove_indentation(lhsv)
426
+ rhsv = remove_indentation(rhsv)
427
+ end
428
+ case [lhst, rhst]
429
+ in [:text, :text] | [:bytes, :bytes]
430
+ [true, lhsv + rhsv]
431
+ in [:bytes, :text]
432
+ [true, (lhsv + rhsv.b).b]
433
+ in [:text, :bytes]
434
+ result = lhsv + rhsv.force_encoding(Encoding::UTF_8)
435
+ if result.valid_encoding?
436
+ [true, result]
437
+ else
438
+ [false, {text_encoding_not_utf8: [lhst, lhsv, rhst, rhsv]}]
439
+ end
440
+ in [:int, _]
441
+ [true, lhsv + Integer(rhsv)]
442
+ in [:float, _]
443
+ [true, lhsv + rhsv]
444
+ end << lhst
445
+ end
446
+ else
447
+ [false]
448
+ end
449
+ end
450
+
451
+ def extract_array(t)
452
+ case t
453
+ in ["ary", ["seq", *members]]
454
+ else
455
+ return [false]
456
+ end
457
+ [true, *members.map { |el|
458
+ case el
459
+ in ["mem", _cut, _any, el4]
460
+ ok, v, vt = extract_value(el4)
461
+ return [false] unless ok
462
+ [v, vt]
463
+ else
464
+ return [false]
465
+ end
466
+ }]
467
+ end
468
+
469
+
470
+ def extract_feature(control, d)
471
+ ok, v, vt = extract_value(control)
472
+ if ok
473
+ nm = v
474
+ det = d
475
+ warn "*** feature controller should be a string: #{control.inspect}" unless :text == vt || :bytes == vt
476
+ else
477
+ ok, *v = extract_array(control)
478
+ if ok && v.size == 2
479
+ nm = v[0][0]
480
+ det = v[1][0]
481
+ warn "*** first element of feature controller should be a string: #{control.inspect}" unless String === nm
482
+ else
483
+ warn "*** feature controller not implemented: #{control.inspect}"
484
+ end
485
+ end
486
+ [nm, det]
487
+ end
488
+
489
+ end
@@ -2,32 +2,6 @@ require_relative "../cddlc.rb"
2
2
 
3
3
  class CDDL
4
4
 
5
- # In each specific case, use the appropriate subset
6
- ESCAPED_STRING_MAPPINGS = Hash[[
7
- ["\x08", "b"],
8
- ["\x09", "t"],
9
- ["\x0A", "n"],
10
- ["\x0C", "f"],
11
- ["\x0D", "r"],
12
- ["\x22", "\""],
13
- ["\x27", "'"],
14
- ["\x2F", "/"],
15
- ["\x5C", "\\"],
16
- ]]
17
-
18
- # TODO: Enable selecting Unicode-friendly variants
19
- def escape_string(s)
20
- s.gsub(/[^\n !#-\[\]-~]/) {|ch|
21
- if m = ESCAPED_STRING_MAPPINGS[ch]
22
- "\\#{m}"
23
- elsif (o = ch.ord) < 0x10000
24
- "\\u#{"%04x" % o}"
25
- else
26
- "\\u{#{"%x" % o}}"
27
- end
28
- }
29
- end
30
-
31
5
  def write_lhs(k, parmnames)
32
6
  if parmnames
33
7
  "#{k}<#{parmnames.join(", ")}>"
@@ -127,8 +101,8 @@ class CDDL
127
101
  in ["mem", cut, t1, t2]
128
102
  [2, "#{write_rhs(t1, 3, indent, pn)} #{cut ? "^" : ""}=> #{write_rhs(t2, 2, indent, pn)}"]
129
103
  # 2->3: work around cddl tool limitation
130
- in ["bytes", t]
131
- [4, t] # XXX not very clean
104
+ in ["bytes", t, tesc]
105
+ [4, bytes_escaped(tesc, t)]
132
106
  in ["text", t]
133
107
  [4, "\"#{escape_string(t)}\""]
134
108
  in ["number", t]
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cddlc
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.5
4
+ version: 0.4.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Carsten Bormann
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2025-01-09 00:00:00.000000000 Z
10
+ date: 2025-02-16 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: bundler
@@ -83,12 +82,25 @@ files:
83
82
  - data/rfc9290.cddl
84
83
  - data/rfc9321.cddl
85
84
  - data/rfc9338.cddl
85
+ - data/rfc9360.cddl
86
86
  - data/rfc9393-concise-swid-tag.cddl
87
87
  - data/rfc9393-sign.cddl
88
88
  - data/rfc9393-sign1.cddl
89
89
  - data/rfc9393-tags.cddl
90
90
  - data/rfc9393.cddl
91
+ - data/rfc9431.cddl
92
+ - data/rfc9526.cddl
93
+ - data/rfc9528.cddl
91
94
  - data/rfc9581.cddl
95
+ - data/rfc9594-error-handling.cddl
96
+ - data/rfc9594-example-extended-scope-aif.cddl
97
+ - data/rfc9594-example-extended-scope-text.cddl
98
+ - data/rfc9594-example-scope-aif.cddl
99
+ - data/rfc9594-example-scope-text.cddl
100
+ - data/rfc9594-get_creds.cddl
101
+ - data/rfc9594-sign_info-parameter.cddl
102
+ - data/rfc9594-sign_info_entry-with-a-gene.cddl
103
+ - data/rfc9597.cddl
92
104
  - lib/cddlc.rb
93
105
  - lib/parser/cddl-util.rb
94
106
  - lib/parser/cddlgrammar.rb
@@ -97,12 +109,12 @@ files:
97
109
  - lib/processor/cddl-flattening.rb
98
110
  - lib/processor/cddl-undefined.rb
99
111
  - lib/processor/cddl-visitor.rb
112
+ - lib/validator/validate.rb
100
113
  - lib/writer/cddl-writer.rb
101
114
  homepage: http://github.com/cabo/cddlc
102
115
  licenses:
103
116
  - MIT
104
117
  metadata: {}
105
- post_install_message:
106
118
  rdoc_options: []
107
119
  require_paths:
108
120
  - lib
@@ -117,8 +129,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
117
129
  - !ruby/object:Gem::Version
118
130
  version: '0'
119
131
  requirements: []
120
- rubygems_version: 3.5.14
121
- signing_key:
132
+ rubygems_version: 3.6.2
122
133
  specification_version: 4
123
134
  summary: CDDL (Concise Data Definition Language) converters and miscellaneous tools
124
135
  test_files: []