cddl 0.10.2 → 0.12.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: 33adef2ddb191ca70e7ad3742684cea679e675fe0169a139c9fa5bcc59abfaa6
4
- data.tar.gz: 7b7b7d2cd0623645d6dca87c5c7a8a3d40540e7eec390411e9cbf3adcc6f7c9d
3
+ metadata.gz: 4b6553d8156f74c2c630f22f6069c537e53b4f34a3d436df02ce73f99e648cb5
4
+ data.tar.gz: 175ddf07c82ef984d40c932eb80f8a8d7cdab54823777d8e9482239251756edc
5
5
  SHA512:
6
- metadata.gz: b4a8ccd11e9dbf1b0ebf2541c03691a94a8430538fbe81db3bd178a2fab1732626114689d305048e88fb54c3336f0e95853c86f8336586d722df90639f82efa6
7
- data.tar.gz: 1c6494a0fa4ead8ea548f61f3af5595dde99707359ce8aa8695c074288248334849f2452cae52620541bb762c9cf3979b159f415a1b787009d1061df54d849b5
6
+ metadata.gz: 357a054cb881ab89c00befe26adfcbf9999b8cbd19f400bdd31eccc39a0eb690e50f4552fc446dfa364086987e1187638983e3579e1a3e06084eb874e1311509
7
+ data.tar.gz: bf8e429cf02926f4c078e7911d73ccffb47ee3c96520f627e33673333061cd85b472e3795a122362fe615c7b73f8ac2ad6ac02290c5f9a0a9ba19ebe3b15fae6
data/bin/cddl CHANGED
@@ -7,10 +7,13 @@ require 'json'
7
7
 
8
8
  Encoding.default_external = "UTF-8" # wake up, smell the coffee
9
9
 
10
+ CDDL_VERSION = Gem.loaded_specs["cddl"].version rescue "unknown-version"
11
+
10
12
  EX_USAGE = 64
11
13
  EX_DATAERR = 65
12
14
 
13
15
  def usage
16
+ warn "cddl tool version #{CDDL_VERSION}"
14
17
  warn "Usage:"
15
18
  warn "#$0 spec.cddl generate [n]"
16
19
  warn "#$0 spec.cddl json-generate [n]"
@@ -19,6 +22,13 @@ def usage
19
22
  exit EX_USAGE
20
23
  end
21
24
 
25
+ def utfify(s)
26
+ s.force_encoding(Encoding::UTF_8).scrub { |c|
27
+ warn "*** replaced invalid UTF-8 byte sequence #{c.inspect} by U+FFFD REPLACEMENT CHARACTER"
28
+ 0xFFFD.chr(Encoding::UTF_8)
29
+ }
30
+ end
31
+
22
32
  def read_arg(arg, remember_fn = true)
23
33
  if arg == "-"
24
34
  $fn = "(stdin)" if remember_fn
@@ -31,7 +41,7 @@ def read_arg(arg, remember_fn = true)
31
41
  end
32
42
 
33
43
  def parser
34
- @parser ||= CDDL::Parser.new(read_arg(ARGV[0], false))
44
+ @parser ||= CDDL::Parser.new(utfify(read_arg(ARGV[0], false)))
35
45
  end
36
46
 
37
47
  def my_pp(v)
@@ -96,7 +106,12 @@ begin
96
106
  instance = if $sequence
97
107
  CBOR.decode("\x9f".b << instance.b << "\xff".b)
98
108
  else
99
- CBOR.decode(instance.b) rescue JSON.load(instance)
109
+ begin
110
+ CBOR.decode(instance.b)
111
+ rescue => e
112
+ warn e.inspect if verbose
113
+ JSON.load(utfify(instance))
114
+ end
100
115
  end
101
116
  instance = instance.cbor_clone if $annotate && ENV["EXPERIMENTAL_ANNOTATE"]
102
117
  warn "#{fn}:" if verbose
@@ -104,6 +119,7 @@ begin
104
119
  # my_pp ann
105
120
  instance.cbor_add_annotations_from(ann) rescue nil
106
121
  my_diag(instance) if $annotate
122
+ warn "ann: #{!!ann}" if verbose
107
123
  unless ann
108
124
  retcode = 1
109
125
  end
data/cddl.gemspec CHANGED
@@ -1,21 +1,23 @@
1
1
  spec = Gem::Specification.new do |s|
2
2
  s.name = 'cddl'
3
- s.version = '0.10.2'
3
+ s.version = '0.12.2'
4
4
  s.summary = "CDDL generator and validator."
5
5
  s.description = %{A parser, generator, and validator for CDDL}
6
6
  s.add_dependency('cbor-diag')
7
- s.add_dependency('abnc')
7
+ s.add_dependency('abnc', '~> 0.1.1')
8
8
  s.add_dependency('json_pure')
9
9
  s.add_dependency('abnftt')
10
10
  s.add_dependency('regexp-examples') # , '1.1.0')
11
11
  s.add_dependency('colorize')
12
12
  s.add_dependency('base32', '~> 0.3')
13
+ s.add_dependency('base45_lite', '~> 1.0')
14
+ s.add_dependency('scanf', '~> 1.0')
13
15
  s.files = `git ls-files`.split("\n").grep(/^[a-z]/)
14
16
  s.files = Dir['lib/**/*.rb'] + %w(cddl.gemspec) + Dir['data/**/*.abnf'] + Dir['data/**/*.cddl'] + Dir['test-data/**/*.cddl'] + Dir['test/**/*.rb']
15
17
  s.require_path = 'lib'
16
18
  s.executables = ['cddl']
17
19
  s.default_executable = 'cddl'
18
- s.required_ruby_version = '>= 2.0'
20
+ s.required_ruby_version = '>= 2.3'
19
21
  s.author = "Carsten Bormann"
20
22
  s.email = "cabo@tzi.org"
21
23
  s.homepage = "http://github.com/cabo/cddl"
data/data/cddl.abnf CHANGED
@@ -13,7 +13,8 @@ genericarg = "<" S type1 S *("," S type1 S ) ">"
13
13
  type = type1 S *("/" S type1 S)
14
14
 
15
15
  type1 = type2 [S (rangeop / annotator) S type2]
16
- / "#" "6" ["." uint] "(" S type S ")" ; note no space!
16
+ / "#" "6" ["." headnumber] "(" S type S ")" ; note no space!
17
+ / "#" "7" ["." headnumber] ; note no space!
17
18
  / "#" DIGIT ["." uint] ; major/ai
18
19
  / "#" ; any
19
20
  / "~" S typename [genericarg]
@@ -22,6 +23,8 @@ type1 = type2 [S (rangeop / annotator) S type2]
22
23
  / "&" S "(" S group S ")"
23
24
  / "&" S groupname [genericarg]
24
25
 
26
+ headnumber = uint / ("<" type ">")
27
+
25
28
  type2 = value
26
29
  / typename [genericarg]
27
30
  / "(" type ")"
@@ -67,27 +70,39 @@ fraction = 1*DIGIT
67
70
  exponent = int
68
71
 
69
72
  text = %x22 *SCHAR %x22
70
- SCHAR = %x20-21 / %x23-7E / SESC
71
- SESC = "\" %x20-7E
73
+ SCHAR = %x20-21 / %x23-5B / %x5D-7E / NONASCII / SESC
74
+
75
+ SESC = "\" ( %x22 / "/" / "\" / ; \" \/ \\
76
+ %x62 / %x66 / %x6E / %x72 / %x74 / ; \b \f \n \r \t
77
+ (%x75 hexchar) ) ; \uXXXX
78
+
79
+ hexchar = "{" (1*"0" [ hexscalar ] / hexscalar) "}" /
80
+ non-surrogate / (high-surrogate "\" %x75 low-surrogate)
81
+ non-surrogate = ((DIGIT / "A"/"B"/"C" / "E"/"F") HEXDIG HEXDIG HEXDIG) /
82
+ ("D" %x30-37 HEXDIG HEXDIG)
83
+ high-surrogate = "D" ("8"/"9"/"A"/"B") HEXDIG HEXDIG
84
+ low-surrogate = "D" ("C"/"D"/"E"/"F") HEXDIG HEXDIG
85
+ hexscalar = "10" HEXDIG HEXDIG HEXDIG HEXDIG / HEXDIG1 HEXDIG HEXDIG HEXDIG HEXDIG
86
+ / non-surrogate / HEXDIG [HEXDIG [HEXDIG]]
72
87
 
73
88
  bytes = [bsqual] %x27 *BCHAR %x27
74
- BCHAR = %x20-26 / %x28-5B / %x5D-7E / SESC / CRLF
75
- bsqual = %x68 ; "h"
76
- / %x62.36.34 ; "b64"
89
+ BCHAR = %x20-26 / %x28-5B / %x5D-7E / NONASCII / SESC / "\'" / CRLF
90
+ bsqual = "h" / "b64"
77
91
 
78
92
  id = EALPHA *(*("-" / ".") (EALPHA / DIGIT))
79
93
  ALPHA = %x41-5A / %x61-7A
80
- EALPHA = %x41-5A / %x61-7A / "@" / "_" / "$"
94
+ EALPHA = ALPHA / "@" / "_" / "$"
81
95
  DIGIT = %x30-39
82
96
  DIGIT1 = %x31-39
83
97
  HEXDIG = DIGIT / "A" / "B" / "C" / "D" / "E" / "F"
98
+ HEXDIG1 = DIGIT1 / "A" / "B" / "C" / "D" / "E" / "F"
84
99
  BINDIG = %x30-31
85
100
 
86
101
  S = *WS
87
102
  WS = SP / NL
88
103
  SP = %x20
89
104
  NL = COMMENT / CRLF
90
- COMMENT = ";" *(SP / VCHAR) CRLF
91
- VCHAR = %x21-7E
105
+ COMMENT = ";" *PCHAR CRLF
106
+ PCHAR = %x20-7E / NONASCII
107
+ NONASCII = %xA0-D7FF / %xE000-10FFFD
92
108
  CRLF = %x0A / %x0D.0A
93
-
data/lib/cddl.rb CHANGED
@@ -2,11 +2,15 @@ require 'abnc'
2
2
  require 'pp'
3
3
  require 'pathname'
4
4
  require 'cbor-pure' unless defined?(CBOR::Tagged)
5
+ require 'half'
6
+ require 'cbor-deterministic'
5
7
  require 'regexp-examples'
6
8
  require 'abnftt'
7
9
  require 'colorize'
8
10
  require 'base64'
9
11
  require 'base32'
12
+ require 'base45_lite'
13
+ require 'scanf'
10
14
 
11
15
  module CDDL
12
16
 
@@ -57,7 +61,7 @@ module CDDL
57
61
  if upto - presult < 100
58
62
  part2 = source_text[presult...upto]
59
63
  else
60
- part2 = source_text[presult, 50] + "......." + source_text[upto-50, 50]
64
+ part2 = source_text[presult, 50] + "......." + (source_text[upto-50, 50] || "")
61
65
  end
62
66
  warn "*** Look for syntax problems around the #{
63
67
  "%%%".colorize(background: :light_yellow)} markers:\n#{
@@ -267,6 +271,21 @@ module CDDL
267
271
  l.map {|l| l.sub(/^ {0,#{indent}}/, "")}.join
268
272
  end
269
273
 
274
+ def unpack_array_to_sequence(content, where)
275
+ # remove the first head
276
+ n = case content.getbyte(0) - (4 << 5)
277
+ when 0..23; 1
278
+ when 24; 2
279
+ when 25; 3
280
+ when 26; 5
281
+ when 27; 9 # unlikely :-)
282
+ else fail ".cborseq sequence for #{where} not an array"
283
+ end
284
+ content[0...n] = ''
285
+ content
286
+ end
287
+
288
+
270
289
  # Memoize a bit here
271
290
 
272
291
  REGEXP_FOR_STRING = Hash.new {|h, k|
@@ -405,9 +424,17 @@ module CDDL
405
424
  when 3
406
425
  gen_word
407
426
  when 6
408
- CBOR::Tagged.new(where[2], generate1(where[3]))
427
+ tn = Integer === where[2] ? where[2] : generate1(where[2])
428
+ unless Integer === tn && tn >= 0
429
+ fail "Can't generate a tag number out of #{where[2]}"
430
+ end
431
+ CBOR::Tagged.new(tn, generate1(where[3]))
409
432
  when 7
410
- case where[2]
433
+ w2 = where[2]
434
+ if Array === w2
435
+ w2 = generate1(w2)
436
+ end
437
+ case w2
411
438
  when nil
412
439
  Math::PI
413
440
  when 20
@@ -416,10 +443,22 @@ module CDDL
416
443
  true
417
444
  when 22
418
445
  nil
419
- when 23
420
- :undefined
421
- when 25, 26, 27
422
- rand()
446
+ when 0..19, 23, 32..255
447
+ CBOR::Simple.new(w2)
448
+ when 25
449
+ while !(hs = Half.encode_from_single_bytes([rand()].pack("g")))
450
+ end
451
+ Half.decode(hs)
452
+ when 26
453
+ while (a = [rand()].pack("g").unpack("g").first).to_cbor.size != 5
454
+ end
455
+ a
456
+ when 27
457
+ while (a = rand()).to_cbor.size != 9
458
+ end
459
+ a
460
+ else
461
+ fail "Can't generate prim #7.#{where[2].inspect}" # XXX retry array generate
423
462
  end
424
463
  else
425
464
  fail "Can't generate prim #{where[1]}"
@@ -534,8 +573,7 @@ module CDDL
534
573
  warn "HUH gen #{where.inspect} #{try.inspect}" unless try.nil?
535
574
  end
536
575
  end
537
- 32.times do
538
- content = generate1(target)
576
+ try_generate(target) do |content|
539
577
  if validate1(content, where)
540
578
  return content
541
579
  end
@@ -561,22 +599,18 @@ module CDDL
561
599
  end
562
600
  enc = bytes ? Encoding::BINARY : Encoding::UTF_8
563
601
  out.force_encoding(enc)
564
- when :cbor, :cborseq
602
+ when :cbor, :cborseq, :cbordet, :cborseqdet
565
603
  unless target == [:prim, 2]
566
604
  fail "Don't know yet how to generate #{where}"
567
605
  end
568
- content = CBOR::encode(generate1(control))
569
- if conop == :cborseq
606
+ input = generate1(control)
607
+ if conop == :cbordet || conop == :cborseqdet
608
+ input = input.cbor_prepare_deterministic
609
+ end
610
+ content = CBOR::encode(input)
611
+ if conop == :cborseq || conop == :cborseqdet
570
612
  # remove the first head
571
- n = case content.getbyte(0) - (4 << 5)
572
- when 0..23; 1
573
- when 24; 2
574
- when 25; 3
575
- when 26; 5
576
- when 27; 9 # unlikely :-)
577
- else fail "Generated .cborseq sequence for #{where} not an array"
578
- end
579
- content[0...n] = ''
613
+ content = unpack_array_to_sequence(content, where)
580
614
  end
581
615
  content
582
616
  when :json
@@ -592,25 +626,28 @@ module CDDL
592
626
  content = Integer(generate1(control)).to_s
593
627
  content
594
628
  when :join
595
- content = generate1(control)
596
- unless Array === content &&
597
- content.all? {|x| String === x &&
598
- ((target == [:prim, 2] && x.encoding == Encoding::BINARY) ||
599
- (target == [:prim, 3] && x.encoding != Encoding::BINARY))}
600
- fail "Don't know yet how to generate #{where}"
601
- end
602
- content = content.join
603
- content
604
- when :b64u, :b64c, :b32, :h32, :hex, :hexlc, :hexuc
605
- content = generate1(control)
606
- unless String === content
607
- fail "Don't know yet how to generate #{where}"
629
+ try_generate(control) do |content|
630
+ if Array === content &&
631
+ content.all? {|x| String === x} &&
632
+ Set[content.map {|x| x.encoding}].size == 1
633
+ content = content.join
634
+ if validate1(content, target)
635
+ return content
636
+ end
637
+ end
608
638
  end
609
- content = case conop
610
- when :b64u
639
+ fail "Don't know yet how to generate #{where}"
640
+ when :b64u, :"b64u-sloppy", :b64c, :"b64c-sloppy",
641
+ :b45, :b32, :h32, :hex, :hexlc, :hexuc
642
+ try_generate(control) do |content|
643
+ if String === content
644
+ content = case conop
645
+ when :b64u, :"b64u-sloppy"
611
646
  Base64.urlsafe_encode64(content, padding: false)
612
- when :b64c
647
+ when :b64c, :"b64c-sloppy"
613
648
  Base64.strict_encode64(content)
649
+ when :b45
650
+ Base45Lite.encode(content)
614
651
  when :b32
615
652
  Base32.table = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"
616
653
  Base32.encode(content).gsub("=", "")
@@ -623,10 +660,32 @@ module CDDL
623
660
  content.unpack("H*")[0]
624
661
  else fail "Cannot happen"
625
662
  end
626
- content
663
+ if validate1(content, target)
664
+ return content
665
+ end
666
+ end
667
+ end
668
+ fail "Not smart enough to generate #{where}"
669
+ when :printf
670
+ try_generate(control) do |content|
671
+ if Array === content && content.size >= 1
672
+ fmt, *data = content
673
+ if String === fmt
674
+ begin
675
+ content = fmt % data
676
+ if validate1(content, target)
677
+ return content
678
+ end
679
+ rescue ArgumentError => e
680
+ # be verbose about mismatches here
681
+ @last_message << "\n** #{fmt.inspect} ArgumentError #{e}"
682
+ end
683
+ end
684
+ end
685
+ end
686
+ fail "Not smart enough to generate #{where}#{@last_message}"
627
687
  when :within, :and
628
- 32.times do
629
- content = generate1(target)
688
+ try_generate(target) do |content|
630
689
  if validate1(content, control)
631
690
  return content
632
691
  elsif conop == :within
@@ -654,22 +713,41 @@ module CDDL
654
713
  end
655
714
  end
656
715
 
716
+ def try_generate(target)
717
+ 32.times do
718
+ content = generate1(target)
719
+ yield content # should return if success
720
+ end
721
+ end
722
+
657
723
  VALUE_TYPE = {text: String, bytes: String, int: Integer, float: Float}
658
724
  SIMPLE_VALUE = {
659
725
  [:prim, 7, 20] => [true, false, :bool],
660
726
  [:prim, 7, 21] => [true, true, :bool],
661
727
  [:prim, 7, 22] => [true, nil, :nil],
662
- [:prim, 7, 23] => [true, :undefined, :undefined],
663
728
  }
729
+ SIMPLE_VALUE_SIMPLE = Set[23] + (0..19) + (32..255)
664
730
 
665
731
  def extract_value(t) # []
666
732
  if vt = VALUE_TYPE[t[0]]
667
733
  [true, t[1], vt]
668
- elsif v = SIMPLE_VALUE[t]
669
- v
734
+ elsif t[0] == :prim && t[1] == 7
735
+ case t2 = t[2]
736
+ when Integer
737
+ when Array
738
+ a, b, c = extract_value(t2)
739
+ if a && c == Integer
740
+ t2 = b
741
+ end
742
+ end
743
+ if v = SIMPLE_VALUE[[:prim, 7, t2]]
744
+ v
745
+ elsif SIMPLE_VALUE_SIMPLE === t2
746
+ [true, CBOR::Simple.new(t2), :simple]
747
+ end
670
748
  elsif t[0] == :anno
671
749
  _, conop, target, control = t
672
- # warn ["EXV0", conop, target, control].inspect
750
+ warn ["EXV0", conop, target, control].inspect if ENV["CDDL_TRACE"]
673
751
  if conop == :cat || conop == :plus || conop == :det
674
752
  ok1, v1, vt1 = extract_value(target)
675
753
  ok2, v2, vt2 = extract_value(control)
@@ -701,6 +779,19 @@ module CDDL
701
779
  }]
702
780
  end
703
781
 
782
+
783
+ def extract_arg0(t)
784
+ return [false] unless t[0] == :array
785
+ [true,
786
+ (el = t[1]
787
+ return [false] unless el[0..3] == [:member, 1, 1, nil]
788
+ ok, v, vt = extract_value(el[4])
789
+ return [false] unless ok
790
+ [v, vt]
791
+ ),
792
+ *t[2..-1]]
793
+ end
794
+
704
795
  def extract_feature(control, d)
705
796
  ok, v, vt = extract_value(control)
706
797
  if ok
@@ -889,6 +980,19 @@ module CDDL
889
980
  }
890
981
  end
891
982
 
983
+ def validate1a_ignore_encoding(d, where)
984
+ if String === d
985
+ validate1a(d.force_encoding(Encoding::BINARY), where) or (
986
+ text = d.force_encoding(Encoding::UTF_8).scrub {
987
+ fail "can't use bytes as text"
988
+ } rescue :NOT_A_TEXT_STRING
989
+ validate1a(text, where)
990
+ )
991
+ else
992
+ validate1a(d, where)
993
+ end
994
+ end
995
+
892
996
  def validate1a(d, where)
893
997
  if ann = validate1(d, where)
894
998
  here = [d, where]
@@ -901,6 +1005,7 @@ module CDDL
901
1005
  end
902
1006
 
903
1007
  OPERATORS = {lt: :<, le: :<=, gt: :>, ge: :>=, eq: :==, ne: :!=}
1008
+ FLOAT_AI_FROM_SIZE = {3 => 25, 5 => 26, 9 => 27}
904
1009
 
905
1010
  def validate1(d, where)
906
1011
  if ENV["CDDL_TRACE"]
@@ -947,7 +1052,7 @@ module CDDL
947
1052
  if conop == :cat || conop == :plus || conop == :det
948
1053
  ok1, v1, vt1 = extract_value(target)
949
1054
  ok2, v2, vt2 = extract_value(control)
950
- # warn ["ANNO0", ok1, v1, vt1, ok2, v2, vt2, d].inspect
1055
+ warn ["ANNO0", ok1, v1, vt1, ok2, v2, vt2, d].inspect if ENV["CDDL_TRACE"]
951
1056
  if ok1 && ok2
952
1057
  v2 = Integer(v2) if vt1 == Integer
953
1058
  if conop == :det
@@ -956,6 +1061,30 @@ module CDDL
956
1061
  end
957
1062
  # warn ["ANNO", ok1, v1, vt1, ok2, v2, vt2, d].inspect
958
1063
  [] if d == v1 + v2 # XXX Focus ArgumentError
1064
+ elsif conop == :cat && String === d
1065
+ warn ["CAT-L", ok1, v1, vt1, ok2, v2, vt2, d].inspect if ENV["CDDL_TRACE"]
1066
+ if ok1 && vt1 == String
1067
+ # d and lhs (v1) need to agree in encoding
1068
+ if d.encoding == v1.encoding
1069
+ if d[0...v1.length] == v1
1070
+ d2 = d[v1.length..-1]
1071
+ warn ["CAT-L1", d2, d2.encoding, control].inspect if ENV["CDDL_TRACE"]
1072
+ validate1a_ignore_encoding(d2, control)
1073
+ end
1074
+ end
1075
+ elsif ok2 && vt2 == String
1076
+ warn ["CAT-R", ok1, v1, vt1, ok2, v2, vt2, d].inspect if ENV["CDDL_TRACE"]
1077
+ if d[-v2.length..-1] == v2
1078
+ d1 = d[0...-v2.length]
1079
+ validate1a(d1, control)
1080
+ end
1081
+ end
1082
+ elsif conop == :plus && Integer === d
1083
+ if ok1 && vt1 == Integer
1084
+ validate1a(d - Integer(v1), control)
1085
+ elsif ok2 && vt2 == Integer
1086
+ validate1a(d - Integer(v2), target)
1087
+ end
959
1088
  end
960
1089
  elsif ann = validate1a(d, target)
961
1090
  case conop
@@ -1040,6 +1169,22 @@ module CDDL
1040
1169
  ann if validate1((CBOR::decode(d) rescue :BAD_CBOR), control)
1041
1170
  when :cborseq
1042
1171
  ann if validate1((CBOR::decode("\x9f".b << d << "\xff".b) rescue :BAD_CBOR), control)
1172
+ when :cbordet
1173
+ decoded = CBOR::decode(d) rescue :BAD_CBOR
1174
+ if d != decoded.to_deterministic_cbor
1175
+ @last_message = "CBOR #{d.inspect} not deterministically encoded"
1176
+ nil
1177
+ else
1178
+ ann if validate1(decoded, control)
1179
+ end
1180
+ when :cborseqdet
1181
+ decoded = CBOR::decode("\x9f".b << d << "\xff".b) rescue :BAD_CBOR
1182
+ if d != unpack_array_to_sequence(decoded.to_deterministic_cbor, d.inspect)
1183
+ @last_message = "CBOR Sequence #{d.inspect} not deterministically encoded"
1184
+ nil
1185
+ else
1186
+ ann if validate1(decoded, control)
1187
+ end
1043
1188
  when :json
1044
1189
  ann if validate1((JSON::load(d) rescue :BAD_JSON), control)
1045
1190
  when :decimal
@@ -1049,30 +1194,98 @@ module CDDL
1049
1194
  end
1050
1195
  )
1051
1196
  when :join
1052
- ok, *v = extract_array(control)
1053
- # warn "@@@JJJ@@@ #{ok.inspect} #{v.inspect}"
1054
- if ok
1055
- expected = v.map {|ve|
1056
- fail "Not a string in #{where}" unless String === ve[0]
1057
- ve[0]
1058
- }.join
1059
- ann if d == expected
1197
+ t = control
1198
+ v = if t[0] == :array
1199
+ t[1..-1].map { |el|
1200
+ if el[0..2] == [:member, 1, 1]
1201
+ ok, v, vt = extract_value(el[4])
1202
+ if ok
1203
+ [true, v, vt]
1204
+ else
1205
+ [false, el[4]]
1206
+ end
1207
+ end
1208
+ }
1209
+ end
1210
+ warn "@@@JOIN@@@ #{v.inspect}" if ENV["CDDL_TRACE"]
1211
+ ok = true
1212
+ if v
1213
+ ix = 0
1214
+ rest = d.dup
1215
+ while ix < v.length
1216
+ if left = v[ix]
1217
+ if left[0] # match constant value first
1218
+ fail "Not a string for #{left.inspect} in #{where}" unless String == left[2]
1219
+ want = left[1]
1220
+ have = rest[0...left[1].length]
1221
+ if want == have
1222
+ rest[0...left[1].length] = ''
1223
+ ix += 1
1224
+ next
1225
+ else
1226
+ fail ".join: want #{want.inspect}, have #{have.inspect}"
1227
+ end
1228
+ else
1229
+ ix += 1
1230
+ if ix == v.length # match remaining
1231
+ warn "@@@JOIN ok in #{ok.inspect} rest #{rest.inspect}" if ENV["CDDL_TRACE"]
1232
+ ok &&= validate1(rest, left[1])
1233
+ warn "@@@JOIN ok out #{ok.inspect}" if ENV["CDDL_TRACE"]
1234
+ # more diag
1235
+ rest = ''
1236
+ next
1237
+ else
1238
+ if mid = v[ix]
1239
+ if mid[0] # have constant value to split over
1240
+ fail "Not a string for #{mid} in #{where}" unless String == mid[2]
1241
+ rest1, rest2 = rest.split(mid[1], 2)
1242
+ if rest2
1243
+ warn "@@@JOIN ok in #{ok.inspect} rest1 #{rest1.inspect}" if ENV["CDDL_TRACE"]
1244
+ ok &&= validate1(rest1, left[1])
1245
+ warn "@@@JOIN ok out #{ok.inspect}" if ENV["CDDL_TRACE"]
1246
+ rest = rest2
1247
+ ix += 1
1248
+ next
1249
+ else
1250
+ fail "Can't find #{mid[1].inspect} in #{rest.inspect}"
1251
+ end
1252
+ else
1253
+ fail "Cannot validate consecutive non-constant members of .join in #{where}"
1254
+ end
1255
+ else
1256
+ fail "Cannot handle .join over #{t[ix+1]} in #{where}"
1257
+ end
1258
+ end
1259
+ end
1260
+ else
1261
+ fail "Cannot handle .join over #{t[ix+1]} in #{where}"
1262
+ end
1263
+ end
1264
+ fail "Can't match #{rest.inspect} for .join in #{where}" if rest != ''
1265
+ ann if ok
1060
1266
  else
1061
1267
  fail "Don't know yet how to validate against #{where}"
1062
1268
  end
1063
- when :b64u, :b64c, :b32, :h32, :hex, :hexlc, :hexuc
1269
+ when :b64u, :"b64u-sloppy", :b64c, :"b64c-sloppy", :b45, :b32, :h32, :hex, :hexlc, :hexuc
1064
1270
  ann if (
1065
1271
  String === d && (
1066
1272
  decoded = case conop
1067
1273
  when :b64u
1068
- /=/ !~ d &&
1274
+ /[+\/=]/ !~ d &&
1069
1275
  Base64.urlsafe_decode64(d)
1276
+ when :"b64u-sloppy"
1277
+ /[-_=]/ !~ d &&
1278
+ Base64.decode64(d.tr("+/", "-_"))
1070
1279
  when :b64c
1071
1280
  Base64.strict_decode64(d)
1281
+ when :"b64c-sloppy"
1282
+ Base64.decode64(d)
1072
1283
  when :b32
1073
1284
  /=/ !~ d &&
1074
1285
  (Base32.table = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567") &&
1075
1286
  Base32.decode(d)
1287
+ when :b45
1288
+ Base45Lite.decode(d)
1076
1289
  when :h32
1077
1290
  /=/ !~ d &&
1078
1291
  (Base32.table = "0123456789ABCDEFGHIJKLMNOPQRSTUV") &&
@@ -1087,6 +1300,21 @@ module CDDL
1087
1300
  end
1088
1301
  ) && validate1(decoded.b, control)
1089
1302
  )
1303
+ when :printf
1304
+ ann if String === d && (
1305
+ ok, fmt, *v = extract_arg0(control)
1306
+ if ok && String == fmt[1]
1307
+ fmt = fmt[0]
1308
+ # warn "** ok #{ok.inspect} fmt #{fmt.inspect} v #{v.inspect}"
1309
+ decoded = d.scanf(fmt) # this is a bit too lenient, so let's do:
1310
+ encode_again = fmt % decoded
1311
+ if encode_again != d
1312
+ warn "** fmt #{fmt.inspect} d #{d.inspect} decoded #{decoded.inspect} encode_again #{encode_again.inspect}"
1313
+ else
1314
+ validate1(decoded, [:array, *v])
1315
+ end
1316
+ end
1317
+ )
1090
1318
  when :within
1091
1319
  if validate1(d, control)
1092
1320
  ann
@@ -1122,20 +1350,35 @@ module CDDL
1122
1350
  end
1123
1351
  d = CBOR::Tagged.new(t, d == 0 ? "".b : d.digits(256).reverse!.pack("C*"))
1124
1352
  end
1125
- CBOR::Tagged === d && d.tag == where[2] && validate1a(d.data, where[3])
1353
+ CBOR::Tagged === d && (
1354
+ Integer === where[2] ? d.tag == where[2] : validate1a(d.tag, where[2])
1355
+ ) && validate1a(d.data, where[3])
1126
1356
  when 7
1127
1357
  t, v = extract_value(where)
1128
1358
  if t
1129
1359
  v.eql? d
1130
1360
  else
1131
- case where[2]
1361
+ case w2 = where[2]
1132
1362
  when nil
1133
- # XXX
1134
- fail
1363
+ Float === d || CBOR::Simple === d || [false, true, nil].include?(d)
1364
+ when Array
1365
+ headnum = case d
1366
+ when Float
1367
+ FLOAT_AI_FROM_SIZE[d.to_cbor.size]
1368
+ when false
1369
+ 20
1370
+ when true
1371
+ 21
1372
+ when nil
1373
+ 22
1374
+ when CBOR::Simple
1375
+ d.value
1376
+ end
1377
+ validate1a(headnum, w2)
1135
1378
  when 25, 26, 27
1136
- Float === d
1379
+ Float === d && FLOAT_AI_FROM_SIZE[d.to_cbor.size] == w2
1137
1380
  else
1138
- fail
1381
+ fail [:val7, d, where].inspect
1139
1382
  end
1140
1383
  end
1141
1384
  else
@@ -1230,6 +1473,18 @@ module CDDL
1230
1473
  end
1231
1474
  end
1232
1475
 
1476
+ STRING_ESCAPES = {
1477
+ "\"" => "\"",
1478
+ "/" => "/",
1479
+ "\\" => "\\",
1480
+ "'" => "'",
1481
+ "b" => "\b",
1482
+ "f" => "\f",
1483
+ "n" => "\n",
1484
+ "r" => "\r",
1485
+ "t" => "\t",
1486
+ }
1487
+
1233
1488
  def value(n)
1234
1489
  # cheat:
1235
1490
  # warn n
@@ -1239,7 +1494,20 @@ module CDDL
1239
1494
  if parts[-1] != ""
1240
1495
  warn "*** Problem decoding byte string #{s.inspect}"
1241
1496
  end
1242
- bsval = parts[0...-1].join("'").gsub(/\\(.)/){$1}
1497
+ bsval = parts[0...-1].join("'").gsub(/\\u([Dd][89AaBb][0-9a-zA-Z]{2})\\u([Dd][CcDdEeFf][0-9a-zA-Z]{2})|\\u([0-9a-zA-Z]{4})|\\u\{([0-9a-zA-Z]+)\}|\r?(\n)|\\([^u])/) {
1498
+ if hex = $3 || $4
1499
+ hex.to_i(16).chr(Encoding::UTF_8)
1500
+ elsif lf = $5
1501
+ lf
1502
+ elsif escaped = $6
1503
+ STRING_ESCAPES[$6] or (
1504
+ fail "Invalid String Escape #{escaped.inspect} in #{s.inspect}"
1505
+ )
1506
+ else
1507
+ ((($1.to_i(16) & 0x3ff) << 10) +
1508
+ ($2.to_i(16) & 0x3ff) + 0x10000).chr(Encoding::UTF_8)
1509
+ end
1510
+ }
1243
1511
  [:bytes,
1244
1512
  case bsqual
1245
1513
  when ""
@@ -1253,7 +1521,17 @@ module CDDL
1253
1521
  end
1254
1522
  ]
1255
1523
  else
1256
- val = eval(n.to_s)
1524
+ if s[-1] == '"'
1525
+ s.gsub!(/\\u([Dd][89AaBb][0-9a-zA-Z]{2})\\u([Dd][CcDdEeFf][0-9a-zA-Z]{2})|\\([^u]|u[0-9a-zA-Z]{4})/) {
1526
+ if $3
1527
+ "\\#$3" # skip this, use eval parser
1528
+ else
1529
+ ((($1.to_i(16) & 0x3ff) << 10) +
1530
+ ($2.to_i(16) & 0x3ff) + 0x10000).chr(Encoding::UTF_8)
1531
+ end
1532
+ }
1533
+ end
1534
+ val = eval(s)
1257
1535
  # warn val
1258
1536
  case val
1259
1537
  when Integer; [:int, val]
@@ -1441,11 +1719,15 @@ module CDDL
1441
1719
 
1442
1720
  BRACE = {"{" => :map, "[" => :array}
1443
1721
  RANGE_EXCLUDE_END = {".." => false, "..." => true}
1444
- SUPPORTED_ANNOTATIONS = [:bits, :size, :regexp, :cbor, :cborseq, :within, :and,
1722
+ SUPPORTED_ANNOTATIONS = [:bits, :size, :regexp,
1723
+ :cbor, :cborseq, :cbordet, :cborseqdet,
1724
+ :within, :and,
1445
1725
  :default, :lt, :le, :gt, :ge, :eq, :ne,
1446
1726
  :feature, :abnf, :abnfb, :det, :cat, :plus,
1447
1727
  :json, :decimal, :join,
1448
- :b64u, :b64c, :b32, :h32, :hex, :hexlc, :hexuc,
1728
+ :b64u, :"b64u-sloppy", :b64c, :"b64c-sloppy",
1729
+ :b45, :b32, :h32, :hex, :hexlc, :hexuc,
1730
+ :printf,
1449
1731
  ]
1450
1732
 
1451
1733
  def type1(n, canbegroup = false)
@@ -1478,6 +1760,13 @@ module CDDL
1478
1760
  when /\A#(\d+)/
1479
1761
  maj = $1.to_i
1480
1762
  s = [:prim, maj, *n.children(:uint).map(&:to_s).map(&:to_i)]
1763
+ if tn = n.headnumber
1764
+ if ui = tn.uint
1765
+ s << ui.to_s.to_i
1766
+ elsif tt = tn.type
1767
+ s << type(tt)
1768
+ end
1769
+ end
1481
1770
  if tagged_type = n.type
1482
1771
  s << type(tagged_type)
1483
1772
  end
@@ -0,0 +1 @@
1
+ a = text .b64c-sloppy 'mnoabcdue=='
@@ -0,0 +1,12 @@
1
+ a = text .b64u bytes
2
+
3
+ ;;gp 10
4
+
5
+ ;;vp "AAAA"
6
+ ;;vp "AAA-"
7
+ ;;vp "AAA_"
8
+ ; fail with 0.11.5:
9
+ ;;vp "AAA+"
10
+ ;;vp "AAA/"
11
+ ;;vp "AAA="
12
+ ;;vp "+kd8"
@@ -1,2 +1,4 @@
1
1
  foo = text .regexp myregexp
2
2
  myregexp = "A" .cat "B"
3
+
4
+ ;;gp 10
@@ -0,0 +1,8 @@
1
+ a1 = '' .cat a2
2
+ a2 = text .b64u ain
3
+ ; a = "e30" .b64u ain
4
+ ain = '{}'
5
+
6
+ ;;gp 3
7
+ ;;vp 'e30'
8
+ ;;vp "e30"
@@ -0,0 +1,3 @@
1
+ a = b<13..15>
2
+ b<T> = #7.<32 .plus T>
3
+ ; cddl dotplus1.cddl gp 10
@@ -0,0 +1,3 @@
1
+ a = d<17..19>
2
+ d<T> = #7.<T .plus 32>
3
+ ; cddl dotplus.cddl gp 10
@@ -0,0 +1,3 @@
1
+ a = d<17..19>
2
+ d<T> = 32.5 .plus T
3
+ ; cddl dotplus4.cddl gp 10
@@ -0,0 +1,3 @@
1
+ a = d<17..19>
2
+ d<T> = T .plus 32.5
3
+ ; cddl dotplus4.cddl gp 10
@@ -0,0 +1,14 @@
1
+ terminal-color = &basecolors
2
+ basecolors = (
3
+ black: 0, red: 1, green: 2, yellow: 3,
4
+ blue: 4, magenta: 5, cyan: 6, white: 7,
5
+ reserved: (8..255) .feature "extension"
6
+ )
7
+ ; extended-color = &(
8
+ ; basecolors,
9
+ ; orange: 8, pink: 9, purple: 10, brown: 11,
10
+ ; )
11
+
12
+ ;;gp 10
13
+ ;;vp 42
14
+ ;;vp 433
@@ -0,0 +1,12 @@
1
+ terminal-color = $terminal-color / text .feature "extension"
2
+ $terminal-color /= &basecolors
3
+ basecolors = (
4
+ black: "black", red: "red", green: "green",
5
+ )
6
+
7
+ pastelcolors = ( pink: "pink", mauve: "mauve", peach: "peach", lavender: "lavender" )
8
+ $terminal-color /= &pastelcolors
9
+
10
+ ;;gp 10
11
+ ;;vp "florp"
12
+ ;;vp "lavender"
@@ -0,0 +1,15 @@
1
+ ; JWT-JWS = text .join ([
2
+ ; b64u<jwt-headers>, ".",
3
+ ; b64u<jwt-payload>, ".",
4
+ ; b64u<jwt-signature>])
5
+ ; ; a = jwt-headers
6
+ ; ; a = b64u<"abc"> ; fails
7
+ ; ; a = b64u<'abc'> ; succeeds
8
+ ; b64u<B> = text .b64u B
9
+ jwt-headers = '' .cat jwt-headers1
10
+ jwt-headers1 = text .json jwt-headermap
11
+ jwt-headermap = { * text => any } ; simplified
12
+ jwt-payload = bytes
13
+ jwt-signature = bytes
14
+
15
+ ;;gp 10
@@ -0,0 +1,15 @@
1
+ JWT-JWS = text .join ([
2
+ b64u<jwt-headers>, ".",
3
+ b64u<jwt-payload>, ".",
4
+ b64u<jwt-signature>])
5
+ ; a = jwt-headers
6
+ ; a = b64u<"abc"> ; fails
7
+ ; a = b64u<'abc'> ; succeeds
8
+ b64u<B> = text .b64u B
9
+ jwt-headers = '' .cat jwt-headers1
10
+ jwt-headers1 = text .json jwt-headermap
11
+ jwt-headermap = { * text => any } ; simplified
12
+ jwt-payload = bytes
13
+ jwt-signature = bytes
14
+
15
+ ;;gp 10
@@ -0,0 +1,7 @@
1
+ start = [a, e, b, c, d]
2
+ a = false
3
+ e = undefined
4
+ b = float32
5
+ ; c = #7.32
6
+ c = #6.<12>(3)
7
+ d = #6.13(4)
@@ -0,0 +1,2 @@
1
+ a = text .printf (["%8d %06d", ~arr])
2
+ arr = [uint, uint]
@@ -0,0 +1 @@
1
+ my_label = text .printf (["0x%x: %a", 4711, 81.5])
@@ -0,0 +1,2 @@
1
+ my_alg_sha = hexlabel<4>
2
+ hexlabel<K> = text .printf (["0x%04x", K])
@@ -0,0 +1,2 @@
1
+ my_alg_sha = hexlabel<int>
2
+ hexlabel<K> = text .printf (["0x%04x", K])
@@ -0,0 +1,2 @@
1
+ any_alg = hexlabel<1..20>
2
+ hexlabel<K> = text .printf (["0x%04x", K])
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cddl
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.10.2
4
+ version: 0.12.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Carsten Bormann
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-02-25 00:00:00.000000000 Z
11
+ date: 2024-06-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: cbor-diag
@@ -28,16 +28,16 @@ dependencies:
28
28
  name: abnc
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - ">="
31
+ - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '0'
33
+ version: 0.1.1
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - ">="
38
+ - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '0'
40
+ version: 0.1.1
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: json_pure
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -108,6 +108,34 @@ dependencies:
108
108
  - - "~>"
109
109
  - !ruby/object:Gem::Version
110
110
  version: '0.3'
111
+ - !ruby/object:Gem::Dependency
112
+ name: base45_lite
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '1.0'
118
+ type: :runtime
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '1.0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: scanf
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: '1.0'
132
+ type: :runtime
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: '1.0'
111
139
  description: A parser, generator, and validator for CDDL
112
140
  email: cabo@tzi.org
113
141
  executables:
@@ -133,7 +161,9 @@ files:
133
161
  - test-data/abnf3.cddl
134
162
  - test-data/ambig.cddl
135
163
  - test-data/b.cddl
164
+ - test-data/b64c-sloppy.cddl
136
165
  - test-data/b64c.cddl
166
+ - test-data/b64u-strict.cddl
137
167
  - test-data/b64u.cddl
138
168
  - test-data/badaddr.cddl
139
169
  - test-data/base32.cddl
@@ -144,6 +174,7 @@ files:
144
174
  - test-data/bpv7a.cddl
145
175
  - test-data/bpv7b.cddl
146
176
  - test-data/cat-re.cddl
177
+ - test-data/catb64.cddl
147
178
  - test-data/cdni-ct.cddl
148
179
  - test-data/complex-occ.cddl
149
180
  - test-data/coral.cddl
@@ -156,7 +187,13 @@ files:
156
187
  - test-data/decimal.cddl
157
188
  - test-data/decimal2.cddl
158
189
  - test-data/det1.cddl
190
+ - test-data/dotplus1.cddl
191
+ - test-data/dotplus2.cddl
192
+ - test-data/dotplus3.cddl
193
+ - test-data/dotplus4.cddl
159
194
  - test-data/dotsize.cddl
195
+ - test-data/enum.cddl
196
+ - test-data/enum1.cddl
160
197
  - test-data/extractor-demo.cddl
161
198
  - test-data/feat1.cddl
162
199
  - test-data/feature-controller.cddl
@@ -184,8 +221,10 @@ files:
184
221
  - test-data/joini.cddl
185
222
  - test-data/joinx.cddl
186
223
  - test-data/json.cddl
224
+ - test-data/json1.cddl
187
225
  - test-data/json2.cddl
188
226
  - test-data/jsoniodef.cddl
227
+ - test-data/jwt.cddl
189
228
  - test-data/kevin5.cddl
190
229
  - test-data/lint1.cddl
191
230
  - test-data/map-group.cddl
@@ -206,6 +245,12 @@ files:
206
245
  - test-data/oidbat.cddl
207
246
  - test-data/patch1.cddl
208
247
  - test-data/plus.cddl
248
+ - test-data/prim.cddl
249
+ - test-data/printf.cddl
250
+ - test-data/printf0.cddl
251
+ - test-data/printf1.cddl
252
+ - test-data/printf2.cddl
253
+ - test-data/printf3.cddl
209
254
  - test-data/reused_named_group.cddl
210
255
  - test-data/sasl.cddl
211
256
  - test-data/sequence.cddl
@@ -238,14 +283,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
238
283
  requirements:
239
284
  - - ">="
240
285
  - !ruby/object:Gem::Version
241
- version: '2.0'
286
+ version: '2.3'
242
287
  required_rubygems_version: !ruby/object:Gem::Requirement
243
288
  requirements:
244
289
  - - ">="
245
290
  - !ruby/object:Gem::Version
246
291
  version: '0'
247
292
  requirements: []
248
- rubygems_version: 3.4.2
293
+ rubygems_version: 3.5.11
249
294
  signing_key:
250
295
  specification_version: 4
251
296
  summary: CDDL generator and validator.