cddl 0.10.2 → 0.12.2

Sign up to get free protection for your applications and to get access to all the features.
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.