From 5bd20ff2f17814e18ef6010c5ecafa000d15af95 Mon Sep 17 00:00:00 2001 From: Ayla Croft Date: Mon, 19 Oct 2026 20:00:25 -0400 Subject: [PATCH 1/2] feat(s138): complete slice 138 (deterministic CBOR and COSE_Sign1 with ML-DSA-87) Signed-off-by: Ayla Croft --- .github/workflows/gate.yml | 5 +- CHANGELOG.md | 19 + REUSE.toml | 16 + docs/07-security-model.md | 33 ++ docs/09-standards-register.md | 1 + docs/regulated/control-mapping.md | 1 + docs/regulated/crypto-inventory.md | 4 + lib/trinity/n6.ex | 24 + lib/trinity/n6/body.ex | 191 ++++++++ lib/trinity/n6/cbor.ex | 295 ++++++++++++ lib/trinity/n6/cose.ex | 293 +++++++++++ lib/trinity/n6/digest.ex | 51 ++ lib/trinity/n6/profile.ex | 129 +++++ scripts/n6_vectors.exs | 454 ++++++++++++++++++ test/fixtures/n6/README.md | 98 ++++ test/fixtures/n6/SHA384SUMS | 337 +++++++++++++ test/fixtures/n6/VERSION | 1 + .../negative/bool_schema_version/true.cbor | Bin 0 -> 6 bytes .../negative/bool_schema_version/true.expect | 1 + .../carrier_schema_key0/envelope_schema.cbor | Bin 0 -> 10 bytes .../envelope_schema.expect | 1 + .../carrier_schema_key0/top_of_range.cbor | Bin 0 -> 10 bytes .../carrier_schema_key0/top_of_range.expect | 1 + .../negative_schema_version/minus_1.cbor | Bin 0 -> 6 bytes .../negative_schema_version/minus_1.expect | 1 + .../negative/no_schema_version/no_key_0.cbor | 1 + .../no_schema_version/no_key_0.expect | 1 + .../n6/body/negative/not_a_map/array.cbor | Bin 0 -> 3 bytes .../n6/body/negative/not_a_map/array.expect | 1 + .../required_key_missing/no_name.cbor | Bin 0 -> 3 bytes .../required_key_missing/no_name.expect | 1 + .../body/negative/set_duplicate/1_twice.cbor | Bin 0 -> 10 bytes .../negative/set_duplicate/1_twice.expect | 1 + .../body/negative/set_unsorted/2_then_1.cbor | Bin 0 -> 10 bytes .../negative/set_unsorted/2_then_1.expect | 1 + .../set_unsorted/longer_head_first.cbor | Bin 0 -> 11 bytes .../set_unsorted/longer_head_first.expect | 1 + .../n6/body/negative/unknown_key/key_19.cbor | Bin 0 -> 8 bytes .../body/negative/unknown_key/key_19.expect | 1 + .../negative/unknown_schema/version_2.cbor | Bin 0 -> 6 bytes .../negative/unknown_schema/version_2.expect | 1 + .../negative/wrong_type/name_as_uint.cbor | Bin 0 -> 5 bytes .../negative/wrong_type/name_as_uint.expect | 1 + .../fixtures/n6/body/positive/all_fields.cbor | Bin 0 -> 13 bytes .../n6/body/positive/all_fields.desc.json | 1 + .../fixtures/n6/body/positive/all_fields.diag | 1 + test/fixtures/n6/body/positive/minimal.cbor | Bin 0 -> 6 bytes .../n6/body/positive/minimal.desc.json | 1 + test/fixtures/n6/body/positive/minimal.diag | 1 + .../positive/set_sorted_by_encoded_bytes.cbor | Bin 0 -> 14 bytes .../set_sorted_by_encoded_bytes.desc.json | 1 + .../positive/set_sorted_by_encoded_bytes.diag | 1 + test/fixtures/n6/body/schema.json | 1 + test/fixtures/n6/digest/abc.in | 1 + test/fixtures/n6/digest/abc.ref.cbor | Bin 0 -> 84 bytes .../digest_alg_not_permitted/sha256.cbor | 1 + .../digest_alg_not_permitted/sha256.expect | 1 + .../digest_alg_not_permitted/shake256.cbor | 1 + .../digest_alg_not_permitted/shake256.expect | 1 + .../sha384_32_bytes.cbor | 1 + .../sha384_32_bytes.expect | 1 + .../negative/digest_not_array/bstr.cbor | 1 + .../negative/digest_not_array/bstr.expect | 1 + .../n6/encoding/negative/bstr_key/h01.cbor | Bin 0 -> 4 bytes .../n6/encoding/negative/bstr_key/h01.expect | 1 + .../negative/duplicate_key/1_twice.cbor | Bin 0 -> 5 bytes .../negative/duplicate_key/1_twice.expect | 1 + .../negative/duplicate_key/nested.cbor | Bin 0 -> 6 bytes .../negative/duplicate_key/nested.expect | 1 + .../n6/encoding/negative/float32/100000.cbor | Bin 0 -> 5 bytes .../encoding/negative/float32/100000.expect | 1 + .../negative/float64_int_valued/1e300.cbor | Bin 0 -> 9 bytes .../negative/float64_int_valued/1e300.expect | 1 + .../nested_in_map_value.cbor | Bin 0 -> 11 bytes .../nested_in_map_value.expect | 1 + .../float_half_1.0/negative_zero.cbor | Bin 0 -> 3 bytes .../float_half_1.0/negative_zero.expect | 1 + .../encoding/negative/float_half_1.0/top.cbor | Bin 0 -> 3 bytes .../negative/float_half_1.0/top.expect | 1 + .../negative/float_half_1.0/zero.cbor | Bin 0 -> 3 bytes .../negative/float_half_1.0/zero.expect | 1 + .../indefinite_length/array_empty.cbor | 1 + .../indefinite_length/array_empty.expect | 1 + .../negative/indefinite_length/bstr.cbor | 1 + .../negative/indefinite_length/bstr.expect | 1 + .../negative/indefinite_length/map.cbor | 1 + .../negative/indefinite_length/map.expect | 1 + .../indefinite_length/nested_in_array.cbor | 1 + .../indefinite_length/nested_in_array.expect | 1 + .../negative/indefinite_length/tstr.cbor | 1 + .../negative/indefinite_length/tstr.expect | 1 + .../minus_2_63_minus_1.cbor | Bin 0 -> 9 bytes .../minus_2_63_minus_1.expect | 1 + .../int_below_dcbor_range/rfc_minus_2_64.cbor | 1 + .../rfc_minus_2_64.expect | 1 + .../negative/map_key_over_u32/two_to_32.cbor | Bin 0 -> 11 bytes .../map_key_over_u32/two_to_32.expect | 1 + .../n6/encoding/negative/nan/double.cbor | Bin 0 -> 9 bytes .../n6/encoding/negative/nan/double.expect | 1 + .../n6/encoding/negative/nan/half.cbor | Bin 0 -> 3 bytes .../n6/encoding/negative/nan/half.expect | 1 + .../n6/encoding/negative/nan/single.cbor | Bin 0 -> 5 bytes .../n6/encoding/negative/nan/single.expect | 1 + .../negative/negative_map_key/minus_1.cbor | Bin 0 -> 3 bytes .../negative/negative_map_key/minus_1.expect | 1 + .../negative/nested_text_key/in_array.cbor | 1 + .../negative/nested_text_key/in_array.expect | 1 + .../nonminimal_head/length_in_two_bytes.cbor | Bin 0 -> 4 bytes .../length_in_two_bytes.expect | 1 + .../nonminimal_head/nested_in_array.cbor | 1 + .../nonminimal_head/nested_in_array.expect | 1 + .../nonminimal_head/uint_0_in_one_byte.cbor | Bin 0 -> 2 bytes .../nonminimal_head/uint_0_in_one_byte.expect | 1 + .../nonminimal_head/uint_23_in_one_byte.cbor | 1 + .../uint_23_in_one_byte.expect | 1 + .../uint_255_in_two_bytes.cbor | Bin 0 -> 3 bytes .../uint_255_in_two_bytes.expect | 1 + .../negative/not_well_formed/ai_28.cbor | 1 + .../negative/not_well_formed/ai_28.expect | 1 + .../negative/not_well_formed/ai_30_nint.cbor | 1 + .../not_well_formed/ai_30_nint.expect | 1 + .../negative/not_well_formed/f8_below_32.cbor | 1 + .../not_well_formed/f8_below_32.expect | 1 + .../negative/not_well_formed/lone_break.cbor | 1 + .../not_well_formed/lone_break.expect | 1 + .../negative/not_well_formed/uint_ai_31.cbor | 1 + .../not_well_formed/uint_ai_31.expect | 1 + .../n6/encoding/negative/simple_16/top.cbor | 1 + .../n6/encoding/negative/simple_16/top.expect | 1 + .../negative/simple_2byte/simple_255.cbor | 1 + .../negative/simple_2byte/simple_255.expect | 1 + .../negative/simple_2byte/simple_32.cbor | 1 + .../negative/simple_2byte/simple_32.expect | 1 + .../negative/swap_keys/24_then_23.cbor | Bin 0 -> 6 bytes .../negative/swap_keys/24_then_23.expect | 1 + .../encoding/negative/swap_keys/2_then_1.cbor | Bin 0 -> 5 bytes .../negative/swap_keys/2_then_1.expect | 1 + .../negative/tag_1_datetime/epoch.cbor | 1 + .../negative/tag_1_datetime/epoch.expect | 1 + .../tag_1_datetime/nested_in_array.cbor | 1 + .../tag_1_datetime/nested_in_array.expect | 1 + .../tag_bignum_small/positive_bignum_1.cbor | 1 + .../tag_bignum_small/positive_bignum_1.expect | 1 + .../negative/tag_bignum_small/rfc_2_64.cbor | Bin 0 -> 11 bytes .../negative/tag_bignum_small/rfc_2_64.expect | 1 + .../negative/tag_cose_sign1/tag_18.cbor | 1 + .../negative/tag_cose_sign1/tag_18.expect | 1 + .../negative/text_bad_utf8/lone_ff.cbor | 1 + .../negative/text_bad_utf8/lone_ff.expect | 1 + .../text_bad_utf8/truncated_sequence.cbor | 1 + .../text_bad_utf8/truncated_sequence.expect | 1 + .../encoding/negative/text_key/rfc_map.cbor | 1 + .../encoding/negative/text_key/rfc_map.expect | 1 + .../n6/encoding/negative/text_key/top.cbor | 1 + .../n6/encoding/negative/text_key/top.expect | 1 + .../negative/text_nfd/e_combining_acute.cbor | 1 + .../text_nfd/e_combining_acute.expect | 1 + .../text_nfd/nested_in_map_value.cbor | 1 + .../text_nfd/nested_in_map_value.expect | 1 + .../negative/text_overlong_utf8/slash.cbor | 1 + .../negative/text_overlong_utf8/slash.expect | 1 + .../negative/text_surrogate/d800.cbor | 1 + .../negative/text_surrogate/d800.expect | 1 + .../encoding/negative/too_deep/depth_17.cbor | 1 + .../negative/too_deep/depth_17.expect | 1 + .../negative/trailing_bytes/after_array.cbor | Bin 0 -> 2 bytes .../trailing_bytes/after_array.expect | 1 + .../negative/trailing_bytes/two_items.cbor | Bin 0 -> 2 bytes .../negative/trailing_bytes/two_items.expect | 1 + .../negative/truncated/array_items.cbor | 1 + .../negative/truncated/array_items.expect | 1 + .../negative/truncated/bstr_body.cbor | 1 + .../negative/truncated/bstr_body.expect | 1 + .../negative/truncated/empty_input.cbor | 0 .../negative/truncated/empty_input.expect | 1 + .../negative/truncated/head_argument.cbor | 1 + .../negative/truncated/head_argument.expect | 1 + .../n6/encoding/negative/undefined/top.cbor | 1 + .../n6/encoding/negative/undefined/top.expect | 1 + .../n6/encoding/positive/array_123.cbor | 1 + .../n6/encoding/positive/array_123.desc.json | 1 + .../n6/encoding/positive/array_123.diag | 1 + .../n6/encoding/positive/array_25.cbor | 2 + .../n6/encoding/positive/array_25.desc.json | 1 + .../n6/encoding/positive/array_25.diag | 1 + .../n6/encoding/positive/array_empty.cbor | 1 + .../encoding/positive/array_empty.desc.json | 1 + .../n6/encoding/positive/array_empty.diag | 1 + .../n6/encoding/positive/array_nested.cbor | 1 + .../encoding/positive/array_nested.desc.json | 1 + .../n6/encoding/positive/array_nested.diag | 1 + .../n6/encoding/positive/bstr_01020304.cbor | 1 + .../encoding/positive/bstr_01020304.desc.json | 1 + .../n6/encoding/positive/bstr_01020304.diag | 1 + .../n6/encoding/positive/bstr_empty.cbor | 1 + .../n6/encoding/positive/bstr_empty.desc.json | 1 + .../n6/encoding/positive/bstr_empty.diag | 1 + .../n6/encoding/positive/depth_16.cbor | 1 + .../n6/encoding/positive/depth_16.desc.json | 1 + .../n6/encoding/positive/depth_16.diag | 1 + test/fixtures/n6/encoding/positive/false.cbor | 1 + .../n6/encoding/positive/false.desc.json | 1 + test/fixtures/n6/encoding/positive/false.diag | 1 + .../n6/encoding/positive/map_1_2_3_4.cbor | 1 + .../encoding/positive/map_1_2_3_4.desc.json | 1 + .../n6/encoding/positive/map_1_2_3_4.diag | 1 + .../n6/encoding/positive/map_empty.cbor | 1 + .../n6/encoding/positive/map_empty.desc.json | 1 + .../n6/encoding/positive/map_empty.diag | 1 + .../n6/encoding/positive/map_key_max_u32.cbor | Bin 0 -> 7 bytes .../positive/map_key_max_u32.desc.json | 1 + .../n6/encoding/positive/map_key_max_u32.diag | 1 + .../positive/map_keys_across_heads.cbor | Bin 0 -> 15 bytes .../positive/map_keys_across_heads.desc.json | 1 + .../positive/map_keys_across_heads.diag | 1 + .../encoding/positive/map_mixed_values.cbor | Bin 0 -> 13 bytes .../positive/map_mixed_values.desc.json | 1 + .../encoding/positive/map_mixed_values.diag | 1 + .../fixtures/n6/encoding/positive/nint_1.cbor | 1 + .../n6/encoding/positive/nint_1.desc.json | 1 + .../fixtures/n6/encoding/positive/nint_1.diag | 1 + .../n6/encoding/positive/nint_10.cbor | 1 + .../n6/encoding/positive/nint_10.desc.json | 1 + .../n6/encoding/positive/nint_10.diag | 1 + .../n6/encoding/positive/nint_100.cbor | 1 + .../n6/encoding/positive/nint_100.desc.json | 1 + .../n6/encoding/positive/nint_100.diag | 1 + .../n6/encoding/positive/nint_1000.cbor | 1 + .../n6/encoding/positive/nint_1000.desc.json | 1 + .../n6/encoding/positive/nint_1000.diag | 1 + .../n6/encoding/positive/nint_min_i64.cbor | 1 + .../encoding/positive/nint_min_i64.desc.json | 1 + .../n6/encoding/positive/nint_min_i64.diag | 1 + test/fixtures/n6/encoding/positive/null.cbor | 1 + .../n6/encoding/positive/null.desc.json | 1 + test/fixtures/n6/encoding/positive/null.diag | 1 + test/fixtures/n6/encoding/positive/true.cbor | 1 + .../n6/encoding/positive/true.desc.json | 1 + test/fixtures/n6/encoding/positive/true.diag | 1 + .../fixtures/n6/encoding/positive/tstr_a.cbor | 1 + .../n6/encoding/positive/tstr_a.desc.json | 1 + .../fixtures/n6/encoding/positive/tstr_a.diag | 1 + .../encoding/positive/tstr_aegean_10151.cbor | 1 + .../positive/tstr_aegean_10151.desc.json | 1 + .../encoding/positive/tstr_aegean_10151.diag | 1 + .../n6/encoding/positive/tstr_empty.cbor | 1 + .../n6/encoding/positive/tstr_empty.desc.json | 1 + .../n6/encoding/positive/tstr_empty.diag | 1 + .../n6/encoding/positive/tstr_ietf.cbor | 1 + .../n6/encoding/positive/tstr_ietf.desc.json | 1 + .../n6/encoding/positive/tstr_ietf.diag | 1 + .../positive/tstr_quote_backslash.cbor | 1 + .../positive/tstr_quote_backslash.desc.json | 1 + .../positive/tstr_quote_backslash.diag | 1 + .../n6/encoding/positive/tstr_u_umlaut.cbor | 1 + .../encoding/positive/tstr_u_umlaut.desc.json | 1 + .../n6/encoding/positive/tstr_u_umlaut.diag | 1 + .../n6/encoding/positive/tstr_water.cbor | 1 + .../n6/encoding/positive/tstr_water.desc.json | 1 + .../n6/encoding/positive/tstr_water.diag | 1 + .../fixtures/n6/encoding/positive/uint_0.cbor | Bin 0 -> 1 bytes .../n6/encoding/positive/uint_0.desc.json | 1 + .../fixtures/n6/encoding/positive/uint_0.diag | 1 + .../fixtures/n6/encoding/positive/uint_1.cbor | 1 + .../n6/encoding/positive/uint_1.desc.json | 1 + .../fixtures/n6/encoding/positive/uint_1.diag | 1 + .../n6/encoding/positive/uint_10.cbor | 1 + .../n6/encoding/positive/uint_10.desc.json | 1 + .../n6/encoding/positive/uint_10.diag | 1 + .../n6/encoding/positive/uint_100.cbor | 1 + .../n6/encoding/positive/uint_100.desc.json | 1 + .../n6/encoding/positive/uint_100.diag | 1 + .../n6/encoding/positive/uint_1000.cbor | 1 + .../n6/encoding/positive/uint_1000.desc.json | 1 + .../n6/encoding/positive/uint_1000.diag | 1 + .../n6/encoding/positive/uint_1000000.cbor | Bin 0 -> 5 bytes .../encoding/positive/uint_1000000.desc.json | 1 + .../n6/encoding/positive/uint_1000000.diag | 1 + .../encoding/positive/uint_1000000000000.cbor | Bin 0 -> 9 bytes .../positive/uint_1000000000000.desc.json | 1 + .../encoding/positive/uint_1000000000000.diag | 1 + .../n6/encoding/positive/uint_23.cbor | 1 + .../n6/encoding/positive/uint_23.desc.json | 1 + .../n6/encoding/positive/uint_23.diag | 1 + .../n6/encoding/positive/uint_24.cbor | 1 + .../n6/encoding/positive/uint_24.desc.json | 1 + .../n6/encoding/positive/uint_24.diag | 1 + .../n6/encoding/positive/uint_25.cbor | 1 + .../n6/encoding/positive/uint_25.desc.json | 1 + .../n6/encoding/positive/uint_25.diag | 1 + .../n6/encoding/positive/uint_255.cbor | 1 + .../n6/encoding/positive/uint_255.desc.json | 1 + .../n6/encoding/positive/uint_255.diag | 1 + .../n6/encoding/positive/uint_256.cbor | Bin 0 -> 3 bytes .../n6/encoding/positive/uint_256.desc.json | 1 + .../n6/encoding/positive/uint_256.diag | 1 + .../n6/encoding/positive/uint_4294967295.cbor | 1 + .../positive/uint_4294967295.desc.json | 1 + .../n6/encoding/positive/uint_4294967295.diag | 1 + .../n6/encoding/positive/uint_4294967296.cbor | Bin 0 -> 9 bytes .../positive/uint_4294967296.desc.json | 1 + .../n6/encoding/positive/uint_4294967296.diag | 1 + .../n6/encoding/positive/uint_65535.cbor | 1 + .../n6/encoding/positive/uint_65535.desc.json | 1 + .../n6/encoding/positive/uint_65535.diag | 1 + .../n6/encoding/positive/uint_65536.cbor | Bin 0 -> 5 bytes .../n6/encoding/positive/uint_65536.desc.json | 1 + .../n6/encoding/positive/uint_65536.diag | 1 + .../n6/encoding/positive/uint_max_u64.cbor | 1 + .../encoding/positive/uint_max_u64.desc.json | 1 + .../n6/encoding/positive/uint_max_u64.diag | 1 + .../negative/alg_missing/no_label_1.cbor | 1 + .../negative/alg_missing/no_label_1.expect | 1 + .../negative/alg_not_mldsa87/eddsa.cbor | 1 + .../negative/alg_not_mldsa87/eddsa.expect | 1 + .../negative/alg_not_mldsa87/mldsa65.cbor | 1 + .../negative/alg_not_mldsa87/mldsa65.expect | 1 + .../header/negative/content_type/label_3.cbor | 1 + .../negative/content_type/label_3.expect | 1 + .../n6/header/negative/crit/label_2.cbor | 1 + .../n6/header/negative/crit/label_2.expect | 1 + .../cwt_on_plain_typ.cbor | 1 + .../cwt_on_plain_typ.expect | 1 + .../header_noncanonical/nonminimal_label.cbor | 1 + .../nonminimal_label.expect | 1 + .../header_noncanonical/trailing_byte.cbor | Bin 0 -> 95 bytes .../header_noncanonical/trailing_byte.expect | 1 + .../header_noncanonical/typ_before_kid.cbor | 1 + .../header_noncanonical/typ_before_kid.expect | 1 + .../negative/kid_missing/no_label_4.cbor | 1 + .../negative/kid_missing/no_label_4.expect | 1 + .../negative/kid_wrong_size/kid_32.cbor | 1 + .../negative/kid_wrong_size/kid_32.expect | 1 + .../n6/header/negative/label_0/label_0.cbor | Bin 0 -> 96 bytes .../n6/header/negative/label_0/label_0.expect | 1 + .../n6/header/negative/label_5/iv.cbor | Bin 0 -> 97 bytes .../n6/header/negative/label_5/iv.expect | 1 + .../label_hash_envelope/label_258.cbor | 1 + .../label_hash_envelope/label_258.expect | 1 + .../negative/label_negative/minus_1.cbor | Bin 0 -> 96 bytes .../negative/label_negative/minus_1.expect | 1 + .../negative/typ_missing/no_label_16.cbor | 1 + .../negative/typ_missing/no_label_16.expect | 1 + .../negative/typ_not_in_table/other.cbor | 1 + .../negative/typ_not_in_table/other.expect | 1 + .../n6/header/negative/typ_uint/typ_1.cbor | 1 + .../n6/header/negative/typ_uint/typ_1.expect | 1 + .../positive/alg_kid_cwt_typ_scitt.cbor | 1 + .../n6/header/positive/alg_kid_typ.cbor | 1 + test/fixtures/n6/header/profile-table.json | 1 + .../n6/thumbprint/acvp_mldsa87_tg3_tc51.kid | 1 + .../n6/thumbprint/acvp_mldsa87_tg3_tc51.pub | Bin 0 -> 2592 bytes test/support/n6_vectors.ex | 87 ++++ test/test_helper.exs | 4 +- test/trinity/n6/body_digest_test.exs | 103 ++++ test/trinity/n6/cbor_test.exs | 192 ++++++++ test/trinity/n6/census_test.exs | 117 +++++ test/trinity/n6/corpus_test.exs | 130 +++++ test/trinity/n6/cose_signing_test.exs | 102 ++++ test/trinity/n6/cose_test.exs | 168 +++++++ test/trinity/n6/vectors_test.exs | 54 +++ 361 files changed, 3188 insertions(+), 3 deletions(-) create mode 100644 lib/trinity/n6.ex create mode 100644 lib/trinity/n6/body.ex create mode 100644 lib/trinity/n6/cbor.ex create mode 100644 lib/trinity/n6/cose.ex create mode 100644 lib/trinity/n6/digest.ex create mode 100644 lib/trinity/n6/profile.ex create mode 100644 scripts/n6_vectors.exs create mode 100644 test/fixtures/n6/README.md create mode 100644 test/fixtures/n6/SHA384SUMS create mode 100644 test/fixtures/n6/VERSION create mode 100644 test/fixtures/n6/body/negative/bool_schema_version/true.cbor create mode 100644 test/fixtures/n6/body/negative/bool_schema_version/true.expect create mode 100644 test/fixtures/n6/body/negative/carrier_schema_key0/envelope_schema.cbor create mode 100644 test/fixtures/n6/body/negative/carrier_schema_key0/envelope_schema.expect create mode 100644 test/fixtures/n6/body/negative/carrier_schema_key0/top_of_range.cbor create mode 100644 test/fixtures/n6/body/negative/carrier_schema_key0/top_of_range.expect create mode 100644 test/fixtures/n6/body/negative/negative_schema_version/minus_1.cbor create mode 100644 test/fixtures/n6/body/negative/negative_schema_version/minus_1.expect create mode 100644 test/fixtures/n6/body/negative/no_schema_version/no_key_0.cbor create mode 100644 test/fixtures/n6/body/negative/no_schema_version/no_key_0.expect create mode 100644 test/fixtures/n6/body/negative/not_a_map/array.cbor create mode 100644 test/fixtures/n6/body/negative/not_a_map/array.expect create mode 100644 test/fixtures/n6/body/negative/required_key_missing/no_name.cbor create mode 100644 test/fixtures/n6/body/negative/required_key_missing/no_name.expect create mode 100644 test/fixtures/n6/body/negative/set_duplicate/1_twice.cbor create mode 100644 test/fixtures/n6/body/negative/set_duplicate/1_twice.expect create mode 100644 test/fixtures/n6/body/negative/set_unsorted/2_then_1.cbor create mode 100644 test/fixtures/n6/body/negative/set_unsorted/2_then_1.expect create mode 100644 test/fixtures/n6/body/negative/set_unsorted/longer_head_first.cbor create mode 100644 test/fixtures/n6/body/negative/set_unsorted/longer_head_first.expect create mode 100644 test/fixtures/n6/body/negative/unknown_key/key_19.cbor create mode 100644 test/fixtures/n6/body/negative/unknown_key/key_19.expect create mode 100644 test/fixtures/n6/body/negative/unknown_schema/version_2.cbor create mode 100644 test/fixtures/n6/body/negative/unknown_schema/version_2.expect create mode 100644 test/fixtures/n6/body/negative/wrong_type/name_as_uint.cbor create mode 100644 test/fixtures/n6/body/negative/wrong_type/name_as_uint.expect create mode 100644 test/fixtures/n6/body/positive/all_fields.cbor create mode 100644 test/fixtures/n6/body/positive/all_fields.desc.json create mode 100644 test/fixtures/n6/body/positive/all_fields.diag create mode 100644 test/fixtures/n6/body/positive/minimal.cbor create mode 100644 test/fixtures/n6/body/positive/minimal.desc.json create mode 100644 test/fixtures/n6/body/positive/minimal.diag create mode 100644 test/fixtures/n6/body/positive/set_sorted_by_encoded_bytes.cbor create mode 100644 test/fixtures/n6/body/positive/set_sorted_by_encoded_bytes.desc.json create mode 100644 test/fixtures/n6/body/positive/set_sorted_by_encoded_bytes.diag create mode 100644 test/fixtures/n6/body/schema.json create mode 100644 test/fixtures/n6/digest/abc.in create mode 100644 test/fixtures/n6/digest/abc.ref.cbor create mode 100644 test/fixtures/n6/digest/negative/digest_alg_not_permitted/sha256.cbor create mode 100644 test/fixtures/n6/digest/negative/digest_alg_not_permitted/sha256.expect create mode 100644 test/fixtures/n6/digest/negative/digest_alg_not_permitted/shake256.cbor create mode 100644 test/fixtures/n6/digest/negative/digest_alg_not_permitted/shake256.expect create mode 100644 test/fixtures/n6/digest/negative/digest_length_mismatch/sha384_32_bytes.cbor create mode 100644 test/fixtures/n6/digest/negative/digest_length_mismatch/sha384_32_bytes.expect create mode 100644 test/fixtures/n6/digest/negative/digest_not_array/bstr.cbor create mode 100644 test/fixtures/n6/digest/negative/digest_not_array/bstr.expect create mode 100644 test/fixtures/n6/encoding/negative/bstr_key/h01.cbor create mode 100644 test/fixtures/n6/encoding/negative/bstr_key/h01.expect create mode 100644 test/fixtures/n6/encoding/negative/duplicate_key/1_twice.cbor create mode 100644 test/fixtures/n6/encoding/negative/duplicate_key/1_twice.expect create mode 100644 test/fixtures/n6/encoding/negative/duplicate_key/nested.cbor create mode 100644 test/fixtures/n6/encoding/negative/duplicate_key/nested.expect create mode 100644 test/fixtures/n6/encoding/negative/float32/100000.cbor create mode 100644 test/fixtures/n6/encoding/negative/float32/100000.expect create mode 100644 test/fixtures/n6/encoding/negative/float64_int_valued/1e300.cbor create mode 100644 test/fixtures/n6/encoding/negative/float64_int_valued/1e300.expect create mode 100644 test/fixtures/n6/encoding/negative/float64_int_valued/nested_in_map_value.cbor create mode 100644 test/fixtures/n6/encoding/negative/float64_int_valued/nested_in_map_value.expect create mode 100644 test/fixtures/n6/encoding/negative/float_half_1.0/negative_zero.cbor create mode 100644 test/fixtures/n6/encoding/negative/float_half_1.0/negative_zero.expect create mode 100644 test/fixtures/n6/encoding/negative/float_half_1.0/top.cbor create mode 100644 test/fixtures/n6/encoding/negative/float_half_1.0/top.expect create mode 100644 test/fixtures/n6/encoding/negative/float_half_1.0/zero.cbor create mode 100644 test/fixtures/n6/encoding/negative/float_half_1.0/zero.expect create mode 100644 test/fixtures/n6/encoding/negative/indefinite_length/array_empty.cbor create mode 100644 test/fixtures/n6/encoding/negative/indefinite_length/array_empty.expect create mode 100644 test/fixtures/n6/encoding/negative/indefinite_length/bstr.cbor create mode 100644 test/fixtures/n6/encoding/negative/indefinite_length/bstr.expect create mode 100644 test/fixtures/n6/encoding/negative/indefinite_length/map.cbor create mode 100644 test/fixtures/n6/encoding/negative/indefinite_length/map.expect create mode 100644 test/fixtures/n6/encoding/negative/indefinite_length/nested_in_array.cbor create mode 100644 test/fixtures/n6/encoding/negative/indefinite_length/nested_in_array.expect create mode 100644 test/fixtures/n6/encoding/negative/indefinite_length/tstr.cbor create mode 100644 test/fixtures/n6/encoding/negative/indefinite_length/tstr.expect create mode 100644 test/fixtures/n6/encoding/negative/int_below_dcbor_range/minus_2_63_minus_1.cbor create mode 100644 test/fixtures/n6/encoding/negative/int_below_dcbor_range/minus_2_63_minus_1.expect create mode 100644 test/fixtures/n6/encoding/negative/int_below_dcbor_range/rfc_minus_2_64.cbor create mode 100644 test/fixtures/n6/encoding/negative/int_below_dcbor_range/rfc_minus_2_64.expect create mode 100644 test/fixtures/n6/encoding/negative/map_key_over_u32/two_to_32.cbor create mode 100644 test/fixtures/n6/encoding/negative/map_key_over_u32/two_to_32.expect create mode 100644 test/fixtures/n6/encoding/negative/nan/double.cbor create mode 100644 test/fixtures/n6/encoding/negative/nan/double.expect create mode 100644 test/fixtures/n6/encoding/negative/nan/half.cbor create mode 100644 test/fixtures/n6/encoding/negative/nan/half.expect create mode 100644 test/fixtures/n6/encoding/negative/nan/single.cbor create mode 100644 test/fixtures/n6/encoding/negative/nan/single.expect create mode 100644 test/fixtures/n6/encoding/negative/negative_map_key/minus_1.cbor create mode 100644 test/fixtures/n6/encoding/negative/negative_map_key/minus_1.expect create mode 100644 test/fixtures/n6/encoding/negative/nested_text_key/in_array.cbor create mode 100644 test/fixtures/n6/encoding/negative/nested_text_key/in_array.expect create mode 100644 test/fixtures/n6/encoding/negative/nonminimal_head/length_in_two_bytes.cbor create mode 100644 test/fixtures/n6/encoding/negative/nonminimal_head/length_in_two_bytes.expect create mode 100644 test/fixtures/n6/encoding/negative/nonminimal_head/nested_in_array.cbor create mode 100644 test/fixtures/n6/encoding/negative/nonminimal_head/nested_in_array.expect create mode 100644 test/fixtures/n6/encoding/negative/nonminimal_head/uint_0_in_one_byte.cbor create mode 100644 test/fixtures/n6/encoding/negative/nonminimal_head/uint_0_in_one_byte.expect create mode 100644 test/fixtures/n6/encoding/negative/nonminimal_head/uint_23_in_one_byte.cbor create mode 100644 test/fixtures/n6/encoding/negative/nonminimal_head/uint_23_in_one_byte.expect create mode 100644 test/fixtures/n6/encoding/negative/nonminimal_head/uint_255_in_two_bytes.cbor create mode 100644 test/fixtures/n6/encoding/negative/nonminimal_head/uint_255_in_two_bytes.expect create mode 100644 test/fixtures/n6/encoding/negative/not_well_formed/ai_28.cbor create mode 100644 test/fixtures/n6/encoding/negative/not_well_formed/ai_28.expect create mode 100644 test/fixtures/n6/encoding/negative/not_well_formed/ai_30_nint.cbor create mode 100644 test/fixtures/n6/encoding/negative/not_well_formed/ai_30_nint.expect create mode 100644 test/fixtures/n6/encoding/negative/not_well_formed/f8_below_32.cbor create mode 100644 test/fixtures/n6/encoding/negative/not_well_formed/f8_below_32.expect create mode 100644 test/fixtures/n6/encoding/negative/not_well_formed/lone_break.cbor create mode 100644 test/fixtures/n6/encoding/negative/not_well_formed/lone_break.expect create mode 100644 test/fixtures/n6/encoding/negative/not_well_formed/uint_ai_31.cbor create mode 100644 test/fixtures/n6/encoding/negative/not_well_formed/uint_ai_31.expect create mode 100644 test/fixtures/n6/encoding/negative/simple_16/top.cbor create mode 100644 test/fixtures/n6/encoding/negative/simple_16/top.expect create mode 100644 test/fixtures/n6/encoding/negative/simple_2byte/simple_255.cbor create mode 100644 test/fixtures/n6/encoding/negative/simple_2byte/simple_255.expect create mode 100644 test/fixtures/n6/encoding/negative/simple_2byte/simple_32.cbor create mode 100644 test/fixtures/n6/encoding/negative/simple_2byte/simple_32.expect create mode 100644 test/fixtures/n6/encoding/negative/swap_keys/24_then_23.cbor create mode 100644 test/fixtures/n6/encoding/negative/swap_keys/24_then_23.expect create mode 100644 test/fixtures/n6/encoding/negative/swap_keys/2_then_1.cbor create mode 100644 test/fixtures/n6/encoding/negative/swap_keys/2_then_1.expect create mode 100644 test/fixtures/n6/encoding/negative/tag_1_datetime/epoch.cbor create mode 100644 test/fixtures/n6/encoding/negative/tag_1_datetime/epoch.expect create mode 100644 test/fixtures/n6/encoding/negative/tag_1_datetime/nested_in_array.cbor create mode 100644 test/fixtures/n6/encoding/negative/tag_1_datetime/nested_in_array.expect create mode 100644 test/fixtures/n6/encoding/negative/tag_bignum_small/positive_bignum_1.cbor create mode 100644 test/fixtures/n6/encoding/negative/tag_bignum_small/positive_bignum_1.expect create mode 100644 test/fixtures/n6/encoding/negative/tag_bignum_small/rfc_2_64.cbor create mode 100644 test/fixtures/n6/encoding/negative/tag_bignum_small/rfc_2_64.expect create mode 100644 test/fixtures/n6/encoding/negative/tag_cose_sign1/tag_18.cbor create mode 100644 test/fixtures/n6/encoding/negative/tag_cose_sign1/tag_18.expect create mode 100644 test/fixtures/n6/encoding/negative/text_bad_utf8/lone_ff.cbor create mode 100644 test/fixtures/n6/encoding/negative/text_bad_utf8/lone_ff.expect create mode 100644 test/fixtures/n6/encoding/negative/text_bad_utf8/truncated_sequence.cbor create mode 100644 test/fixtures/n6/encoding/negative/text_bad_utf8/truncated_sequence.expect create mode 100644 test/fixtures/n6/encoding/negative/text_key/rfc_map.cbor create mode 100644 test/fixtures/n6/encoding/negative/text_key/rfc_map.expect create mode 100644 test/fixtures/n6/encoding/negative/text_key/top.cbor create mode 100644 test/fixtures/n6/encoding/negative/text_key/top.expect create mode 100644 test/fixtures/n6/encoding/negative/text_nfd/e_combining_acute.cbor create mode 100644 test/fixtures/n6/encoding/negative/text_nfd/e_combining_acute.expect create mode 100644 test/fixtures/n6/encoding/negative/text_nfd/nested_in_map_value.cbor create mode 100644 test/fixtures/n6/encoding/negative/text_nfd/nested_in_map_value.expect create mode 100644 test/fixtures/n6/encoding/negative/text_overlong_utf8/slash.cbor create mode 100644 test/fixtures/n6/encoding/negative/text_overlong_utf8/slash.expect create mode 100644 test/fixtures/n6/encoding/negative/text_surrogate/d800.cbor create mode 100644 test/fixtures/n6/encoding/negative/text_surrogate/d800.expect create mode 100644 test/fixtures/n6/encoding/negative/too_deep/depth_17.cbor create mode 100644 test/fixtures/n6/encoding/negative/too_deep/depth_17.expect create mode 100644 test/fixtures/n6/encoding/negative/trailing_bytes/after_array.cbor create mode 100644 test/fixtures/n6/encoding/negative/trailing_bytes/after_array.expect create mode 100644 test/fixtures/n6/encoding/negative/trailing_bytes/two_items.cbor create mode 100644 test/fixtures/n6/encoding/negative/trailing_bytes/two_items.expect create mode 100644 test/fixtures/n6/encoding/negative/truncated/array_items.cbor create mode 100644 test/fixtures/n6/encoding/negative/truncated/array_items.expect create mode 100644 test/fixtures/n6/encoding/negative/truncated/bstr_body.cbor create mode 100644 test/fixtures/n6/encoding/negative/truncated/bstr_body.expect create mode 100644 test/fixtures/n6/encoding/negative/truncated/empty_input.cbor create mode 100644 test/fixtures/n6/encoding/negative/truncated/empty_input.expect create mode 100644 test/fixtures/n6/encoding/negative/truncated/head_argument.cbor create mode 100644 test/fixtures/n6/encoding/negative/truncated/head_argument.expect create mode 100644 test/fixtures/n6/encoding/negative/undefined/top.cbor create mode 100644 test/fixtures/n6/encoding/negative/undefined/top.expect create mode 100644 test/fixtures/n6/encoding/positive/array_123.cbor create mode 100644 test/fixtures/n6/encoding/positive/array_123.desc.json create mode 100644 test/fixtures/n6/encoding/positive/array_123.diag create mode 100644 test/fixtures/n6/encoding/positive/array_25.cbor create mode 100644 test/fixtures/n6/encoding/positive/array_25.desc.json create mode 100644 test/fixtures/n6/encoding/positive/array_25.diag create mode 100644 test/fixtures/n6/encoding/positive/array_empty.cbor create mode 100644 test/fixtures/n6/encoding/positive/array_empty.desc.json create mode 100644 test/fixtures/n6/encoding/positive/array_empty.diag create mode 100644 test/fixtures/n6/encoding/positive/array_nested.cbor create mode 100644 test/fixtures/n6/encoding/positive/array_nested.desc.json create mode 100644 test/fixtures/n6/encoding/positive/array_nested.diag create mode 100644 test/fixtures/n6/encoding/positive/bstr_01020304.cbor create mode 100644 test/fixtures/n6/encoding/positive/bstr_01020304.desc.json create mode 100644 test/fixtures/n6/encoding/positive/bstr_01020304.diag create mode 100644 test/fixtures/n6/encoding/positive/bstr_empty.cbor create mode 100644 test/fixtures/n6/encoding/positive/bstr_empty.desc.json create mode 100644 test/fixtures/n6/encoding/positive/bstr_empty.diag create mode 100644 test/fixtures/n6/encoding/positive/depth_16.cbor create mode 100644 test/fixtures/n6/encoding/positive/depth_16.desc.json create mode 100644 test/fixtures/n6/encoding/positive/depth_16.diag create mode 100644 test/fixtures/n6/encoding/positive/false.cbor create mode 100644 test/fixtures/n6/encoding/positive/false.desc.json create mode 100644 test/fixtures/n6/encoding/positive/false.diag create mode 100644 test/fixtures/n6/encoding/positive/map_1_2_3_4.cbor create mode 100644 test/fixtures/n6/encoding/positive/map_1_2_3_4.desc.json create mode 100644 test/fixtures/n6/encoding/positive/map_1_2_3_4.diag create mode 100644 test/fixtures/n6/encoding/positive/map_empty.cbor create mode 100644 test/fixtures/n6/encoding/positive/map_empty.desc.json create mode 100644 test/fixtures/n6/encoding/positive/map_empty.diag create mode 100644 test/fixtures/n6/encoding/positive/map_key_max_u32.cbor create mode 100644 test/fixtures/n6/encoding/positive/map_key_max_u32.desc.json create mode 100644 test/fixtures/n6/encoding/positive/map_key_max_u32.diag create mode 100644 test/fixtures/n6/encoding/positive/map_keys_across_heads.cbor create mode 100644 test/fixtures/n6/encoding/positive/map_keys_across_heads.desc.json create mode 100644 test/fixtures/n6/encoding/positive/map_keys_across_heads.diag create mode 100644 test/fixtures/n6/encoding/positive/map_mixed_values.cbor create mode 100644 test/fixtures/n6/encoding/positive/map_mixed_values.desc.json create mode 100644 test/fixtures/n6/encoding/positive/map_mixed_values.diag create mode 100644 test/fixtures/n6/encoding/positive/nint_1.cbor create mode 100644 test/fixtures/n6/encoding/positive/nint_1.desc.json create mode 100644 test/fixtures/n6/encoding/positive/nint_1.diag create mode 100644 test/fixtures/n6/encoding/positive/nint_10.cbor create mode 100644 test/fixtures/n6/encoding/positive/nint_10.desc.json create mode 100644 test/fixtures/n6/encoding/positive/nint_10.diag create mode 100644 test/fixtures/n6/encoding/positive/nint_100.cbor create mode 100644 test/fixtures/n6/encoding/positive/nint_100.desc.json create mode 100644 test/fixtures/n6/encoding/positive/nint_100.diag create mode 100644 test/fixtures/n6/encoding/positive/nint_1000.cbor create mode 100644 test/fixtures/n6/encoding/positive/nint_1000.desc.json create mode 100644 test/fixtures/n6/encoding/positive/nint_1000.diag create mode 100644 test/fixtures/n6/encoding/positive/nint_min_i64.cbor create mode 100644 test/fixtures/n6/encoding/positive/nint_min_i64.desc.json create mode 100644 test/fixtures/n6/encoding/positive/nint_min_i64.diag create mode 100644 test/fixtures/n6/encoding/positive/null.cbor create mode 100644 test/fixtures/n6/encoding/positive/null.desc.json create mode 100644 test/fixtures/n6/encoding/positive/null.diag create mode 100644 test/fixtures/n6/encoding/positive/true.cbor create mode 100644 test/fixtures/n6/encoding/positive/true.desc.json create mode 100644 test/fixtures/n6/encoding/positive/true.diag create mode 100644 test/fixtures/n6/encoding/positive/tstr_a.cbor create mode 100644 test/fixtures/n6/encoding/positive/tstr_a.desc.json create mode 100644 test/fixtures/n6/encoding/positive/tstr_a.diag create mode 100644 test/fixtures/n6/encoding/positive/tstr_aegean_10151.cbor create mode 100644 test/fixtures/n6/encoding/positive/tstr_aegean_10151.desc.json create mode 100644 test/fixtures/n6/encoding/positive/tstr_aegean_10151.diag create mode 100644 test/fixtures/n6/encoding/positive/tstr_empty.cbor create mode 100644 test/fixtures/n6/encoding/positive/tstr_empty.desc.json create mode 100644 test/fixtures/n6/encoding/positive/tstr_empty.diag create mode 100644 test/fixtures/n6/encoding/positive/tstr_ietf.cbor create mode 100644 test/fixtures/n6/encoding/positive/tstr_ietf.desc.json create mode 100644 test/fixtures/n6/encoding/positive/tstr_ietf.diag create mode 100644 test/fixtures/n6/encoding/positive/tstr_quote_backslash.cbor create mode 100644 test/fixtures/n6/encoding/positive/tstr_quote_backslash.desc.json create mode 100644 test/fixtures/n6/encoding/positive/tstr_quote_backslash.diag create mode 100644 test/fixtures/n6/encoding/positive/tstr_u_umlaut.cbor create mode 100644 test/fixtures/n6/encoding/positive/tstr_u_umlaut.desc.json create mode 100644 test/fixtures/n6/encoding/positive/tstr_u_umlaut.diag create mode 100644 test/fixtures/n6/encoding/positive/tstr_water.cbor create mode 100644 test/fixtures/n6/encoding/positive/tstr_water.desc.json create mode 100644 test/fixtures/n6/encoding/positive/tstr_water.diag create mode 100644 test/fixtures/n6/encoding/positive/uint_0.cbor create mode 100644 test/fixtures/n6/encoding/positive/uint_0.desc.json create mode 100644 test/fixtures/n6/encoding/positive/uint_0.diag create mode 100644 test/fixtures/n6/encoding/positive/uint_1.cbor create mode 100644 test/fixtures/n6/encoding/positive/uint_1.desc.json create mode 100644 test/fixtures/n6/encoding/positive/uint_1.diag create mode 100644 test/fixtures/n6/encoding/positive/uint_10.cbor create mode 100644 test/fixtures/n6/encoding/positive/uint_10.desc.json create mode 100644 test/fixtures/n6/encoding/positive/uint_10.diag create mode 100644 test/fixtures/n6/encoding/positive/uint_100.cbor create mode 100644 test/fixtures/n6/encoding/positive/uint_100.desc.json create mode 100644 test/fixtures/n6/encoding/positive/uint_100.diag create mode 100644 test/fixtures/n6/encoding/positive/uint_1000.cbor create mode 100644 test/fixtures/n6/encoding/positive/uint_1000.desc.json create mode 100644 test/fixtures/n6/encoding/positive/uint_1000.diag create mode 100644 test/fixtures/n6/encoding/positive/uint_1000000.cbor create mode 100644 test/fixtures/n6/encoding/positive/uint_1000000.desc.json create mode 100644 test/fixtures/n6/encoding/positive/uint_1000000.diag create mode 100644 test/fixtures/n6/encoding/positive/uint_1000000000000.cbor create mode 100644 test/fixtures/n6/encoding/positive/uint_1000000000000.desc.json create mode 100644 test/fixtures/n6/encoding/positive/uint_1000000000000.diag create mode 100644 test/fixtures/n6/encoding/positive/uint_23.cbor create mode 100644 test/fixtures/n6/encoding/positive/uint_23.desc.json create mode 100644 test/fixtures/n6/encoding/positive/uint_23.diag create mode 100644 test/fixtures/n6/encoding/positive/uint_24.cbor create mode 100644 test/fixtures/n6/encoding/positive/uint_24.desc.json create mode 100644 test/fixtures/n6/encoding/positive/uint_24.diag create mode 100644 test/fixtures/n6/encoding/positive/uint_25.cbor create mode 100644 test/fixtures/n6/encoding/positive/uint_25.desc.json create mode 100644 test/fixtures/n6/encoding/positive/uint_25.diag create mode 100644 test/fixtures/n6/encoding/positive/uint_255.cbor create mode 100644 test/fixtures/n6/encoding/positive/uint_255.desc.json create mode 100644 test/fixtures/n6/encoding/positive/uint_255.diag create mode 100644 test/fixtures/n6/encoding/positive/uint_256.cbor create mode 100644 test/fixtures/n6/encoding/positive/uint_256.desc.json create mode 100644 test/fixtures/n6/encoding/positive/uint_256.diag create mode 100644 test/fixtures/n6/encoding/positive/uint_4294967295.cbor create mode 100644 test/fixtures/n6/encoding/positive/uint_4294967295.desc.json create mode 100644 test/fixtures/n6/encoding/positive/uint_4294967295.diag create mode 100644 test/fixtures/n6/encoding/positive/uint_4294967296.cbor create mode 100644 test/fixtures/n6/encoding/positive/uint_4294967296.desc.json create mode 100644 test/fixtures/n6/encoding/positive/uint_4294967296.diag create mode 100644 test/fixtures/n6/encoding/positive/uint_65535.cbor create mode 100644 test/fixtures/n6/encoding/positive/uint_65535.desc.json create mode 100644 test/fixtures/n6/encoding/positive/uint_65535.diag create mode 100644 test/fixtures/n6/encoding/positive/uint_65536.cbor create mode 100644 test/fixtures/n6/encoding/positive/uint_65536.desc.json create mode 100644 test/fixtures/n6/encoding/positive/uint_65536.diag create mode 100644 test/fixtures/n6/encoding/positive/uint_max_u64.cbor create mode 100644 test/fixtures/n6/encoding/positive/uint_max_u64.desc.json create mode 100644 test/fixtures/n6/encoding/positive/uint_max_u64.diag create mode 100644 test/fixtures/n6/header/negative/alg_missing/no_label_1.cbor create mode 100644 test/fixtures/n6/header/negative/alg_missing/no_label_1.expect create mode 100644 test/fixtures/n6/header/negative/alg_not_mldsa87/eddsa.cbor create mode 100644 test/fixtures/n6/header/negative/alg_not_mldsa87/eddsa.expect create mode 100644 test/fixtures/n6/header/negative/alg_not_mldsa87/mldsa65.cbor create mode 100644 test/fixtures/n6/header/negative/alg_not_mldsa87/mldsa65.expect create mode 100644 test/fixtures/n6/header/negative/content_type/label_3.cbor create mode 100644 test/fixtures/n6/header/negative/content_type/label_3.expect create mode 100644 test/fixtures/n6/header/negative/crit/label_2.cbor create mode 100644 test/fixtures/n6/header/negative/crit/label_2.expect create mode 100644 test/fixtures/n6/header/negative/cwt_without_scitt_typ/cwt_on_plain_typ.cbor create mode 100644 test/fixtures/n6/header/negative/cwt_without_scitt_typ/cwt_on_plain_typ.expect create mode 100644 test/fixtures/n6/header/negative/header_noncanonical/nonminimal_label.cbor create mode 100644 test/fixtures/n6/header/negative/header_noncanonical/nonminimal_label.expect create mode 100644 test/fixtures/n6/header/negative/header_noncanonical/trailing_byte.cbor create mode 100644 test/fixtures/n6/header/negative/header_noncanonical/trailing_byte.expect create mode 100644 test/fixtures/n6/header/negative/header_noncanonical/typ_before_kid.cbor create mode 100644 test/fixtures/n6/header/negative/header_noncanonical/typ_before_kid.expect create mode 100644 test/fixtures/n6/header/negative/kid_missing/no_label_4.cbor create mode 100644 test/fixtures/n6/header/negative/kid_missing/no_label_4.expect create mode 100644 test/fixtures/n6/header/negative/kid_wrong_size/kid_32.cbor create mode 100644 test/fixtures/n6/header/negative/kid_wrong_size/kid_32.expect create mode 100644 test/fixtures/n6/header/negative/label_0/label_0.cbor create mode 100644 test/fixtures/n6/header/negative/label_0/label_0.expect create mode 100644 test/fixtures/n6/header/negative/label_5/iv.cbor create mode 100644 test/fixtures/n6/header/negative/label_5/iv.expect create mode 100644 test/fixtures/n6/header/negative/label_hash_envelope/label_258.cbor create mode 100644 test/fixtures/n6/header/negative/label_hash_envelope/label_258.expect create mode 100644 test/fixtures/n6/header/negative/label_negative/minus_1.cbor create mode 100644 test/fixtures/n6/header/negative/label_negative/minus_1.expect create mode 100644 test/fixtures/n6/header/negative/typ_missing/no_label_16.cbor create mode 100644 test/fixtures/n6/header/negative/typ_missing/no_label_16.expect create mode 100644 test/fixtures/n6/header/negative/typ_not_in_table/other.cbor create mode 100644 test/fixtures/n6/header/negative/typ_not_in_table/other.expect create mode 100644 test/fixtures/n6/header/negative/typ_uint/typ_1.cbor create mode 100644 test/fixtures/n6/header/negative/typ_uint/typ_1.expect create mode 100644 test/fixtures/n6/header/positive/alg_kid_cwt_typ_scitt.cbor create mode 100644 test/fixtures/n6/header/positive/alg_kid_typ.cbor create mode 100644 test/fixtures/n6/header/profile-table.json create mode 100644 test/fixtures/n6/thumbprint/acvp_mldsa87_tg3_tc51.kid create mode 100644 test/fixtures/n6/thumbprint/acvp_mldsa87_tg3_tc51.pub create mode 100644 test/support/n6_vectors.ex create mode 100644 test/trinity/n6/body_digest_test.exs create mode 100644 test/trinity/n6/cbor_test.exs create mode 100644 test/trinity/n6/census_test.exs create mode 100644 test/trinity/n6/corpus_test.exs create mode 100644 test/trinity/n6/cose_signing_test.exs create mode 100644 test/trinity/n6/cose_test.exs create mode 100644 test/trinity/n6/vectors_test.exs diff --git a/.github/workflows/gate.yml b/.github/workflows/gate.yml index f19b18e..bfc3220 100644 --- a/.github/workflows/gate.yml +++ b/.github/workflows/gate.yml @@ -380,7 +380,8 @@ jobs: # runtime in CI that signs ML-DSA-87 (the gate runners link 3.0; in the mode, Red Hat's # provider refuses it), so the ML-DSA-87 path runs here end to end: the planted-step cases, # a regulated node that boots with ML-DSA-87 receipts and whose boot receipt verifies, and the - # signer's key shapes. TRINITY_REQUIRE_MLDSA87_RUNTIME makes test_helper raise if the runtime + # signer's key shapes. Slice 138 adds N6's COSE_Sign1 signing and verification with ML-DSA-87. + # TRINITY_REQUIRE_MLDSA87_RUNTIME makes test_helper raise if the runtime # cannot sign, rather than excluding the cases it came to run. # # It runs last, from a fresh suite state. The suite keeps its receipt keys in tmp/test_keys @@ -394,4 +395,4 @@ jobs: TRINITY_REQUIRE_MLDSA87_RUNTIME: "1" run: | rm -rf tmp trinity_test.db* trinity_test_receipts.db* - mix test test/trinity/crypto/preflight_test.exs test/trinity/toolchain_node_test.exs test/trinity/receipts/signer_test.exs + mix test test/trinity/crypto/preflight_test.exs test/trinity/toolchain_node_test.exs test/trinity/receipts/signer_test.exs test/trinity/n6/cose_signing_test.exs test/trinity/n6/cose_test.exs diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f931f4..4d33880 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -47,6 +47,25 @@ refusing in its own operating system process; the floor at every boundary and a generated versions; every step of the ML-DSA-87 check failing by name; and, on the FIPS image with the mode off, the planted failures against the real cryptographic library. +### `slice/138`: deterministic CBOR and COSE_Sign1 with ML-DSA-87 + +Trinity can now build and check signed statements in the N6 profile: deterministic CBOR (RFC 8949 +section 4.2.1, narrowed to integers, byte and text strings, arrays, integer-keyed maps and three +simple values) inside COSE_Sign1, signed with ML-DSA-87 (RFC 9964). The decoder refuses any byte +string that is not exactly the deterministic encoding of such a value and names the first defect; +a statement's key id is the RFC 9679 thumbprint of its public key; a verifier that cannot run +ML-DSA-87 says it cannot judge rather than failing or passing. Bytes received from elsewhere are +referenced by their SHA-384 digest, never re-encoded first. The profile is provisional (v0, draft +r3), so this is groundwork: nothing Trinity signed before changes, and no statement it signs uses +the new format yet. The project's vectors for it, written from RFC 8949's own examples and from +CBOR heads by hand, are checked in with a `sha384sum` manifest. + +Verified by the test suite: every vector, encoded and decoded, at the top level and nested; a +property over generated values; each limit at its value and one past it; the header rules one by +one; signing and verifying with NIST's published ML-DSA-87 test key on the FIPS image with the mode +off; a decoder planted to accept one defect class failing that class and no other; and an opt-in +run of 1,000,000 generated statements whose 13,000,000 mutants were all refused. + ## 2026-10-08 ### `slice/136`: the web pages ask who is there diff --git a/REUSE.toml b/REUSE.toml index 3815662..bf5229c 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -84,3 +84,19 @@ path = ["priv/kat/wycheproof/**"] precedence = "override" SPDX-FileCopyrightText = "Google LLC and the C2SP Wycheproof contributors" SPDX-License-Identifier = "Apache-2.0" + +# Slice 138. The N6 vectors `scripts/n6_vectors.exs` writes: CBOR, diagnostic notation and expected +# outcomes, none of which can carry a comment. The RFC 8949 Appendix A encodings among them are +# credited in each vector's description. The thumbprint vector's public key is NIST's, the ACVP +# case under `priv/kat/acvp/` above, and is annotated as that file is. +[[annotations]] +path = ["test/fixtures/n6/**"] +precedence = "aggregate" +SPDX-FileCopyrightText = "Sudo Apt Holdings LLC" +SPDX-License-Identifier = "Apache-2.0" + +[[annotations]] +path = ["test/fixtures/n6/thumbprint/*.pub"] +precedence = "override" +SPDX-FileCopyrightText = "National Institute of Standards and Technology" +SPDX-License-Identifier = "CC0-1.0" diff --git a/docs/07-security-model.md b/docs/07-security-model.md index ed6df74..516ff10 100644 --- a/docs/07-security-model.md +++ b/docs/07-security-model.md @@ -546,6 +546,39 @@ fail-open. Where it signs today: a runtime linked to OpenSSL 3.5 or later with t mode, on the image's current FIPS provider, it does not; a provider validated with ML-DSA-87 (CMVP #5523) is what would change that. +## Signed statements in one byte form (Slice 138, as built) + +The rule: every statement Trinity constructs and signs is deterministic CBOR inside COSE_Sign1, +and bytes someone else produced are hashed exactly as received, never parsed and re-encoded first. +Two encoders that agree on a value's meaning but not its bytes produce two signatures for one +statement, and a verifier that re-encodes before checking verifies its own bytes, not the +signer's. `Trinity.N6` is the layer that rule needs; nothing signs through it yet, and receipts keep +their slice 024 format until a later slice moves them. + +- **The codec refuses rather than repairs.** `Trinity.N6.CBOR.decode/1` accepts a byte string only + if it is exactly the deterministic encoding of a value in the subset: shortest heads, definite + lengths, unsigned-integer map keys in bytewise order with no duplicate, no float, tag or other + simple value, NFC text, within the four limits. It names the first defect and returns nothing + from an input that fails. A forged length larger than the input is refused before anything is + read. +- **The header is closed.** The protected header carries exactly `alg` -50 (ML-DSA-87), `kid` and + `typ`, plus CWT claims for a SCITT-renderable `typ`; content type, `crit`, the hash-envelope + labels and every other label are refused, and the unprotected header must be empty, so no + unsigned field can change what a statement claims. +- **The key id is the key.** `kid` is the RFC 9679 SHA-384 thumbprint over the public key and + `alg`, and the verifier recomputes it from the key it holds, so a key cannot be presented under + another algorithm or another key's name. +- **Cannot judge is not a pass.** A runtime that cannot run ML-DSA-87 (FIPS mode on a provider + that refuses it, or OpenSSL 3.0) makes `verify/2` answer exit 5, no trust, with + `alg_unavailable`, never a crash and never a pass; an unknown `kid` is exit 5 too. + +`test/trinity/n6/census_test.exs` holds every hashing and signing call in `lib/` with a class. +Four sites re-encode data from elsewhere before hashing it, which the rule forbids, and are +declared there rather than changed here: the tool-definition drift digest, the approval +fingerprint, the MCP server's retry binding, and an effect receipt's digest of non-binary tool +output. Each needs the exact bytes it hashes, which the layer that receives them does not keep +today. + ## The effect population, published (Slice 027, as built) The set of things that can cause an effect is small, derived from the tree, and published as diff --git a/docs/09-standards-register.md b/docs/09-standards-register.md index 57a5de6..6dede1f 100644 --- a/docs/09-standards-register.md +++ b/docs/09-standards-register.md @@ -21,6 +21,7 @@ document, a PROOF.md line) or `none`; the status; who decides the status. Status | FIPS 140-3 validated cryptography in FIPS mode | OTP built with `--enable-fips` against a validated provider; `crypto:info_fips()` enabled; slice 024's signer selects an approved algorithm or denies | slice 003 (FIPS build leg), slice 024 AC8 | 003 landed 2026-09-20: the gate runs in the mode on every push (`fips` job, docs/fips-leg.md); the provider is the one the distribution ships and names, and the word validated is the certificate's, not this tree's; 024 AC8 still `:unknown` | owner, then an assessor | | CNSA 1.0 signature suite (ECDSA P-384, SHA-384) | slice 024 amendment 2, FIPS mode selection | slice 024 AC8 | `:unknown`; 024 landed 2026-09-20 (tag `slice/024`) and is approved, so the gate on this row is gone and the status is the owner's to set | owner | | CNSA 2.0 readiness (ML-DSA-87) | slice 024 amendment 6, behind the same seam, compile-conditional on OpenSSL 3.5 or later; never default | none yet | `not claimed` | owner; the word validated waits on the CMVP listing of the provider that carries it | +| Signed statements as deterministic CBOR in COSE_Sign1 with ML-DSA-87 (RFC 8949 section 4.2.1, RFC 9052, RFC 9964; key id RFC 9679) | `Trinity.N6`, slice 138: the codec, the header rules, the thumbprint, signing and verifying, under the N6 profile v0, draft r3, which is provisional | `test/trinity/n6/`; `test/fixtures/n6/` with its `SHA384SUMS` | `not claimed`: no statement Trinity signs uses it yet, and the profile has no published vectors | owner | | FIPS 140-2 certificates on the Historical list from 2026-09-22 | not applicable: Trinity cites no 140-2 module | none | `real-world dependency` (deployment's modules) | deployment | | TLS floor 1.2 in FIPS mode | one README line and a test pinning it | slice 002 AC3 | `:unknown`; 002 landed 2026-09-23 (tag `slice/002`) and is approved, so the gate on this row is gone and the status is the owner's to set | owner | | Envelope MAC in FIPS mode is an approved algorithm | HMAC-SHA256 or AES-GCM in the MRTR envelope Trinity mints | slice 061 | `:unknown`; 061 landed 2026-09-22 (tag `slice/061`) and is approved, so the gate on this row is gone and the status is the owner's to set | owner | diff --git a/docs/regulated/control-mapping.md b/docs/regulated/control-mapping.md index 288b836..caddd90 100644 --- a/docs/regulated/control-mapping.md +++ b/docs/regulated/control-mapping.md @@ -118,6 +118,7 @@ register does not claim is never Trinity's. | `SC-12` | Custody of the signing key is not addressed: a key in a file shows the records were not altered by anything without read access to it, and nothing about who held it | `docs/10-assurance-case.md` | none | the deployment's | Custody of the signing key | CRM "Custody of the signing key" | | `SC-13` | Signing picks its algorithm by the runtime's mode: Ed25519 by default, ECDSA P-384 with SHA-384 in FIPS mode, where Ed25519 reports itself unavailable. The gate and the suite run in FIPS mode on every push | `lib/trinity/receipts/signer.ex`, `docs/fips-leg.md`, `docs/regulated/crypto-inventory.md` | `test/trinity/receipts/signer_test.exs` "outside FIPS mode the default is Ed25519; in FIPS mode (the fips leg) it is P-384"; `test/fips/receipts_test.exs` "the mode is on, Ed25519 reports unavailable, P-384 is selected and the boot receipt names it" | shared | A validated cryptographic module on the host, established from its certificate and the running binary | Register "FIPS 140-3 validated cryptography in FIPS mode": :unknown; Register "FIPS 140-2 certificates on the Historical list from 2026-09-22": real-world dependency | | `SC-13` | ML-DSA-87 is offered for receipts only where a known-answer check passes through `:crypto` in the mode the node is in: NIST's ACVP keyGen case (the published secret key yields the published public key), Wycheproof's verify cases (a valid signature, a bit-flipped one and one with malformed hints), then a fresh key. Not where the algorithm is merely listed: OTP lists no ML-DSA type in FIPS mode whatever the provider can do. A regulated node configured for ML-DSA-87 that fails the check refuses to boot naming the step; the boot receipt records the check's answer | `lib/trinity/crypto/preflight.ex`, `priv/kat/`, `lib/trinity/receipts/signer/mldsa87.ex`, `lib/trinity/receipts/key_custody.ex` | `Trinity.Crypto.Preflight.mldsa87/1`; `test/trinity/crypto/preflight_test.exs` "a provider that derives the wrong public key fails at :keygen_kat"; `test/trinity/crypto/preflight_test.exs` "a verifier that accepts Wycheproof's bit-flipped signature fails at :sigver_kat, naming the case"; `test/trinity/crypto/preflight_test.exs` "the ML-DSA-87 signer is available exactly when the preflight passes (F-137-1)"; `test/trinity/toolchain_node_test.exs` "a failing preflight refuses the boot, naming the step" | Trinity's | none | none | +| `SC-13` | Signed statements have one byte form: deterministic CBOR inside COSE_Sign1 with ML-DSA-87. The decoder refuses any input that is not exactly the deterministic encoding of an allowed value, naming the first defect; the header admits only `alg` -50, the key's RFC 9679 thumbprint as `kid`, and `typ`; a verifier that cannot run ML-DSA-87 answers no trust, never a pass. A census classifies every hashing and signing call in the tree and names the sites that still re-encode data from elsewhere before hashing it. No statement Trinity signs uses the form yet | `lib/trinity/n6/`, `test/fixtures/n6/`, `scripts/n6_vectors.exs` | `test/trinity/n6/cbor_test.exs` "every negative vector is refused with its code"; `test/trinity/n6/cose_test.exs` "every negative header is refused with its code"; `test/trinity/n6/cose_test.exs` "kid for ACVP's published key is SHA-384 over exactly the RFC 9679 input"; `test/trinity/n6/cose_signing_test.exs` "one flipped byte in the protected header, the payload or the signature is invalid"; `test/trinity/n6/census_test.exs` "every hashing and signing call in lib/ is in the census, with its count and a class" | Trinity's | none | none | | `SC-23` | A request whose `Host` is not the loopback or the configured host is refused before routing, and the websocket origin check is an explicit list in every environment; under `TRINITY_PROFILE=regulated` an origin check of `false` or `:conn` refuses to boot, because a DNS rebinding page controls both | `lib/trinity_web/plugs/host_allow_list.ex`, `lib/trinity/profile.ex` | `Trinity.Profile.check_origin/2`; `test/trinity_web/auth/host_and_origin_test.exs` "a foreign Host is 403 before routing, wherever it points"; `test/trinity_web/auth/host_and_origin_test.exs` "check_origin is an explicit list in dev, test and prod" | shared | Setting `PHX_HOST` to the host users reach | CRM "Refusing a request whose Host is not this deployment's" | | `SC-23` | The state an MCP client carries between rounds is sealed with AES-256-GCM: a replay, one tampered byte, an expired envelope and a state bound to other arguments are refused | `lib/trinity/mcp/server/envelope.ex` | `test/trinity/mcp/server_mrtr_test.exs` "a replay inside the partition, one tampered byte, an expired envelope and a state bound to other arguments are refused; a missing state is a first call" | Trinity's | none | Register "Envelope MAC in FIPS mode is an approved algorithm": :unknown | | `SC-24` | Failure denies: an unavailable signer denies every effect, a tool absent from the catalogue or a decision other than allow is denied before anything runs, and an authority adapter that cannot be loaded refuses the boot | `lib/trinity/effects.ex`, `lib/trinity/authority/selection.ex` | `test/trinity/effects/membrane_test.exs` "a :catalog tool absent from the catalog, or a decision other than allow, is denied before anything runs"; `test/trinity/authority/selection_test.exs` "an absent module is refused as not loaded, by name" | Trinity's | none | Register "Nothing fails open": :unknown | diff --git a/docs/regulated/crypto-inventory.md b/docs/regulated/crypto-inventory.md index 5cd6eea..700a18c 100644 --- a/docs/regulated/crypto-inventory.md +++ b/docs/regulated/crypto-inventory.md @@ -27,6 +27,10 @@ to that provider, on that host, not to this software. **Anything not established | Device identifier | `lib/trinity/receipts/clock.ex` | `:crypto.strong_rand_bytes/1` | Not applicable | Random, names nothing about the host | | JWK thumbprint | `lib/trinity/receipts/signer.ex` | `:crypto.hash(:sha256, …)` | Not applicable | RFC 7638 | | Canonical JSON | `lib/trinity/receipts/envelope.ex:15` | `Jcs.encode/1` | Not applicable | RFC 8785, not a cryptographic primitive | +| N6 statement signing (slice 138) | `lib/trinity/n6/cose.ex` | `:crypto.sign(:mldsa87, :none, …)` and `:crypto.verify/5`, FIPS 204, pure, empty context | **UNKNOWN** | Over RFC 9052's ToBeSigned. Not used by any statement yet. Where the runtime refuses ML-DSA-87 (FIPS mode on Red Hat's provider; OpenSSL 3.0) verification answers no trust, never a crash | +| N6 key id | `lib/trinity/n6/cose.ex` | `:crypto.hash(:sha384, …)` | Not applicable | RFC 9679 thumbprint over RFC 9964's AKP members | +| N6 foreign-bytes digest | `lib/trinity/n6/digest.ex` | `:crypto.hash(:sha384, …)` | **UNKNOWN** | Over bytes exactly as received, never re-encoded | +| Deterministic CBOR | `lib/trinity/n6/cbor.ex` | pure Elixir | Not applicable | RFC 8949 section 4.2.1, not a cryptographic primitive | ## Why every certificate says UNKNOWN diff --git a/lib/trinity/n6.ex b/lib/trinity/n6.ex new file mode 100644 index 0000000..0873e20 --- /dev/null +++ b/lib/trinity/n6.ex @@ -0,0 +1,24 @@ +# SPDX-FileCopyrightText: Sudo Apt Holdings LLC +# SPDX-License-Identifier: Apache-2.0 +defmodule Trinity.N6 do + @moduledoc """ + The N6 profile: deterministic CBOR and COSE_Sign1 with ML-DSA-87 for signed statements + (slice 138). + + The rule it serves: every statement this tree constructs and signs is deterministic CBOR inside + COSE_Sign1, and bytes someone else produced are hashed exactly as received, never re-encoded. + + * `Trinity.N6.CBOR`: the encoder and the strict decoder (rules E1 to E11); + * `Trinity.N6.Body`: a statement body against its declared schema; + * `Trinity.N6.Digest`: SHA-384 references to foreign bytes; + * `Trinity.N6.COSE`: the header rules, the key thumbprint, signing and verifying; + * `Trinity.N6.Profile`: the pinned draft, its limits, and every refusal code. + + This is a top-level boundary with no dependency on the rest of Trinity, as `Trinity.Keys` is, + so it stays extractable: the compiler refuses a call from here into any other Trinity module. + The profile is provisional (v0, draft r3); see `Trinity.N6.Profile`. Nothing Trinity signed + before this boundary existed is changed by it. + """ + + use Boundary, top_level?: true, deps: [], exports: [CBOR, Body, Digest, COSE, Profile] +end diff --git a/lib/trinity/n6/body.ex b/lib/trinity/n6/body.ex new file mode 100644 index 0000000..83258ce --- /dev/null +++ b/lib/trinity/n6/body.ex @@ -0,0 +1,191 @@ +# SPDX-FileCopyrightText: Sudo Apt Holdings LLC +# SPDX-License-Identifier: Apache-2.0 +defmodule Trinity.N6.Body do + @moduledoc """ + The body rules of the N6 profile (its section 3; slice 138): the shape every signed statement's + body takes, checked against a declared schema. + + A body is a map. Key 0 is the schema version, an unsigned integer (major type 0 only), so it + always encodes first. The pair of statement kind and schema version must be one the caller + declares; within it every key must be declared (there is no "ignore unknown" mode: a new field is + a new schema version), every required key present, and every value of its declared type. Key 0 + values 0xFFFF0000 to 0xFFFFFFFF are reserved for carrier structures and are refused in a body. + + An array is a sequence unless its field is declared a set. A set is encoded sorted by the encoded + bytes of its elements, with no duplicates. The bytes alone cannot say which arrays are sets, + which is why that check lives here and not in `Trinity.N6.CBOR`. + + ## Schemas + + %{kind: "trinity.test", version: 1, + fields: %{16 => {:name, :tstr, :required}, 18 => {:members, {:set, :uint}, :optional}}} + + Types: `:uint`, `:int`, `:tstr`, `:bstr`, `:bool`, `:any`, `{:array, type}`, `{:set, type}`, + `:digest` (`Trinity.N6.Digest`), and `{:map, fields}` for a nested map with no schema version. + + Checks run in this order, each answering the first failure: not a map (`body_not_map`); key 0 + missing (`schema_version_missing`), not an unsigned integer (`key_not_uint`), reserved + (`schema_reserved`), unknown with this kind (`unknown_schema`); then keys in ascending order, + undeclared (`unknown_key`); then required keys (`required_key_missing`); then each value in key + order (`wrong_type`, `set_not_canonical`, or a digest's own code). `body_not_map`, + `required_key_missing` and `wrong_type` are this tree's codes where the profile has none yet. + """ + + alias Trinity.N6.{CBOR, Digest, Profile} + + @reserved 0xFFFF_0000..0xFFFF_FFFF + + @typedoc "A field type." + @type type :: + :uint + | :int + | :tstr + | :bstr + | :bool + | :any + | :digest + | {:array, type()} + | {:set, type()} + | {:map, %{optional(non_neg_integer()) => field()}} + + @typedoc "A field: its name, its type, and whether it must be present." + @type field :: {atom(), type(), :required | :optional} + + @typedoc "A schema: one statement kind at one schema version." + @type schema :: %{ + kind: String.t(), + version: non_neg_integer(), + fields: %{optional(pos_integer()) => field()} + } + + @doc """ + Decodes bytes as a body of `kind` under one of `schemas`: the strict decode of + `Trinity.N6.CBOR`, then `check/3`. + """ + @spec decode(binary(), String.t(), [schema()]) :: + {:ok, map(), schema()} | {:error, Profile.code(), non_neg_integer() | nil} + def decode(bytes, kind, schemas) when is_binary(bytes) do + with {:ok, value} <- CBOR.decode(bytes), + {:ok, schema} <- check(value, kind, schemas) do + {:ok, value, schema} + else + {:error, code, offset} -> {:error, code, offset} + {:error, code} -> {:error, code, nil} + end + end + + @doc """ + Encodes a body after checking it, so a body this tree builds is one it would accept. The + decode limits apply too: a body that could not be read back is not built. + """ + @spec encode(map(), String.t(), [schema()]) :: {:ok, binary()} | {:error, Profile.code()} + def encode(value, kind, schemas) do + with {:ok, _schema} <- check(value, kind, schemas), + {:ok, bytes} <- CBOR.encode(value), + {:ok, _} <- CBOR.decode(bytes) do + {:ok, bytes} + else + {:error, code, _offset} -> {:error, code} + {:error, code} -> {:error, code} + end + end + + @doc "Checks a decoded value against the body rules and the schema its key 0 names." + @spec check(CBOR.value(), String.t(), [schema()]) :: {:ok, schema()} | {:error, Profile.code()} + def check(value, _kind, _schemas) when not is_map(value), do: {:error, :body_not_map} + + def check(value, kind, schemas) do + with {:ok, version} <- version(value), + {:ok, schema} <- schema(kind, version, schemas), + :ok <- fields(Map.delete(value, 0), schema.fields) do + {:ok, schema} + end + end + + # Key 0 is the schema version at the top level only; a nested map has no version key, so every + # key it holds is checked against its fields. + defp fields(map, fields) do + with :ok <- known_keys(map, fields), + :ok <- required(map, fields), + do: types(map, fields) + end + + defp version(%{0 => v}) when is_integer(v) and v in @reserved, do: {:error, :schema_reserved} + defp version(%{0 => v}) when is_integer(v) and v >= 0, do: {:ok, v} + defp version(%{0 => _}), do: {:error, :key_not_uint} + defp version(_), do: {:error, :schema_version_missing} + + defp schema(kind, version, schemas) do + case Enum.find(schemas, &(&1.kind == kind and &1.version == version)) do + nil -> {:error, :unknown_schema} + schema -> {:ok, schema} + end + end + + defp known_keys(value, fields) do + case value |> Map.keys() |> Enum.sort() |> Enum.find(&(not is_map_key(fields, &1))) do + nil -> :ok + _key -> {:error, :unknown_key} + end + end + + defp required(value, fields) do + missing = for {key, {_name, _type, :required}} <- fields, not is_map_key(value, key), do: key + if missing == [], do: :ok, else: {:error, :required_key_missing} + end + + defp types(value, fields) do + value + |> Enum.sort() + |> Enum.find_value(:ok, fn {key, v} -> + {_name, type, _presence} = Map.fetch!(fields, key) + + case of_type(v, type) do + :ok -> nil + error -> error + end + end) + end + + defp of_type(v, :uint) when is_integer(v) and v >= 0, do: :ok + defp of_type(v, :int) when is_integer(v), do: :ok + defp of_type({:text, _}, :tstr), do: :ok + defp of_type({:bytes, _}, :bstr), do: :ok + defp of_type(v, :bool) when is_boolean(v), do: :ok + defp of_type(_v, :any), do: :ok + + defp of_type(v, :digest) do + case Digest.check(v) do + :ok -> :ok + {:error, code} -> {:error, code} + end + end + + defp of_type(items, {:array, type}) when is_list(items), do: all_of(items, type) + + defp of_type(items, {:set, type}) when is_list(items) do + with :ok <- all_of(items, type), do: canonical_set(items) + end + + defp of_type(map, {:map, nested}) when is_map(map), do: fields(map, nested) + + defp of_type(_v, _type), do: {:error, :wrong_type} + + defp all_of(items, type) do + Enum.find_value(items, :ok, fn item -> + case of_type(item, type) do + :ok -> nil + error -> error + end + end) + end + + defp canonical_set(items) do + encoded = Enum.map(items, &CBOR.encode!/1) + + strictly_ascending? = + encoded |> Enum.chunk_every(2, 1, :discard) |> Enum.all?(fn [a, b] -> a < b end) + + if strictly_ascending?, do: :ok, else: {:error, :set_not_canonical} + end +end diff --git a/lib/trinity/n6/cbor.ex b/lib/trinity/n6/cbor.ex new file mode 100644 index 0000000..71c65fd --- /dev/null +++ b/lib/trinity/n6/cbor.ex @@ -0,0 +1,295 @@ +# SPDX-FileCopyrightText: Sudo Apt Holdings LLC +# SPDX-License-Identifier: Apache-2.0 +defmodule Trinity.N6.CBOR do + @moduledoc """ + The N6 subset of CBOR: an encoder that can only produce the deterministic encoding, and a + decoder that refuses every byte string that is not exactly the deterministic encoding of a + value in the subset (slice 138; the profile's rules E1 to E11, `Trinity.N6.Profile`). + + ## Values + + * integers in [-2^63, 2^64-1]; + * `{:bytes, binary}` for a byte string and `{:text, binary}` for a text string, which must be + valid UTF-8 in Unicode NFC. A bare binary is refused: in signed data, bytes and text differ in + meaning, and an untagged binary that happens to be valid UTF-8 would otherwise change type; + * lists (arrays); + * maps whose keys are integers in 0..2^32-1; + * `true`, `false` and `nil`. + + The encoding is RFC 8949 section 4.2.1's core deterministic encoding: every argument in its + shortest form, definite lengths only, map keys in the bytewise order of their encodings (for + unsigned integer keys in shortest form, the same as numeric order). + + ## Decoding + + `decode/1` reads the input once, left to right and depth first, and stops at the first defect, + answering its code and byte offset. Nothing from an input that fails is returned. At each head + the checks run in this order, which is this tree's provisional answer to the profile's open + question of precedence (its VB1): + + 1. the head byte exists (`truncated`); + 2. additional information 28 to 30, or 31 where no indefinite form exists, or a lone break + (`not_well_formed`); 31 on a byte string, text string, array or map (`indefinite_length`); + 3. a tag (`tag_forbidden`); a float (`float_forbidden`); a simple value other than false, true + and null (`simple_forbidden`, or `not_well_formed` for `0xf8` below 32); + 4. the argument's bytes exist (`truncated`) and it is in its shortest form (`noncanonical`); + 5. the item count (`too_many_items`); + 6. a negative integer below -2^63 (`int_out_of_range`); a string's bytes exist (`truncated`); + a text string's length, validity and form (`text_too_long`, `invalid_utf8`, `not_nfc`); a + container's depth (`too_deep`); + 7. inside a map, for each key: its major type (`key_not_uint`), the key itself by the rules + above, its value at most 2^32-1 (`key_not_uint`), and its encoding against the previous + key's: equal is `duplicate_key`, lower is `noncanonical` (the profile's W2). + + The whole input is checked against the size limit first (`too_large`), and bytes after the + top-level item are `trailing_bytes`. Last, the value is re-encoded and compared byte for byte + with the input (E11). With the checks above complete, that comparison cannot fail; if it ever + does, it is answered as `noncanonical`, and the suite treats it as a rule this pass missed. + """ + + import Bitwise + + alias Trinity.N6.Profile + + @typedoc "A value in the N6 subset." + @type value :: + integer() + | {:bytes, binary()} + | {:text, binary()} + | [value()] + | %{optional(non_neg_integer()) => value()} + | boolean() + | nil + + @uint64_max 0xFFFF_FFFF_FFFF_FFFF + @int64_min -0x8000_0000_0000_0000 + @nint_arg_max 0x7FFF_FFFF_FFFF_FFFF + @key_max 0xFFFF_FFFF + + @limits Profile.limits() + @max_depth @limits.depth + @max_items @limits.items + @max_text @limits.text + @max_size @limits.size + + ## Encoding + + @doc """ + The deterministic encoding of a value, or the code of the first part of it outside the subset. + Limits are not applied: they bound what is decoded, not what is built. + """ + @spec encode(value()) :: {:ok, binary()} | {:error, Profile.code()} + def encode(value) do + {:ok, value |> enc() |> IO.iodata_to_binary()} + catch + {:n6_encode, code} -> {:error, code} + end + + @doc "As `encode/1`, raising `ArgumentError` with the code on a value outside the subset." + @spec encode!(value()) :: binary() + def encode!(value) do + case encode(value) do + {:ok, bytes} -> + bytes + + {:error, code} -> + raise ArgumentError, "not an N6 value (#{code}): #{inspect(value, limit: 8)}" + end + end + + defp enc(n) when is_integer(n) and n >= 0 and n <= @uint64_max, do: head(0, n) + defp enc(n) when is_integer(n) and n < 0 and n >= @int64_min, do: head(1, -1 - n) + defp enc(n) when is_integer(n), do: throw({:n6_encode, :int_out_of_range}) + defp enc(f) when is_float(f), do: throw({:n6_encode, :float_forbidden}) + defp enc(false), do: <<0xF4>> + defp enc(true), do: <<0xF5>> + defp enc(nil), do: <<0xF6>> + defp enc({:bytes, b}) when is_binary(b), do: [head(2, byte_size(b)), b] + + defp enc({:text, s}) when is_binary(s) do + case text_defect(s) do + nil -> [head(3, byte_size(s)), s] + code -> throw({:n6_encode, code}) + end + end + + defp enc(items) when is_list(items), do: [head(4, length(items)) | Enum.map(items, &enc/1)] + + defp enc(map) when is_map(map) do + keys = Map.keys(map) + + unless Enum.all?(keys, &(is_integer(&1) and &1 >= 0 and &1 <= @key_max)), + do: throw({:n6_encode, :key_not_uint}) + + [ + head(5, map_size(map)) + | for(k <- Enum.sort(keys), do: [head(0, k), enc(Map.fetch!(map, k))]) + ] + end + + defp enc(_other), do: throw({:n6_encode, :value_not_n6}) + + defp head(major, n) when n < 24, do: <> + defp head(major, n) when n <= 0xFF, do: <> + defp head(major, n) when n <= 0xFFFF, do: <> + defp head(major, n) when n <= 0xFFFF_FFFF, do: <> + defp head(major, n), do: <> + + # Erlang's UTF-8 matching refuses overlong forms and surrogates, which is what E8 asks. + defp text_defect(s) do + cond do + not String.valid?(s) -> :invalid_utf8 + :unicode.characters_to_nfc_binary(s) != s -> :not_nfc + true -> nil + end + end + + ## Decoding + + @doc """ + The value a byte string is the deterministic encoding of, or the code and byte offset of its + first defect. See the moduledoc for the order of checks. + """ + @spec decode(binary()) :: {:ok, value()} | {:error, Profile.code(), non_neg_integer()} + def decode(bin) when is_binary(bin) and byte_size(bin) > @max_size, do: {:error, :too_large, 0} + + def decode(bin) when is_binary(bin) do + with {:ok, value} <- pass(bin) do + if encode(value) == {:ok, bin}, do: {:ok, value}, else: {:error, :noncanonical, 0} + end + end + + # The one-pass checks without E11's round trip. Public for the suite only, which holds that + # every refusal is found here and none is left to the round trip. + @doc false + @spec pass(binary()) :: {:ok, value()} | {:error, Profile.code(), non_neg_integer()} + def pass(bin) when is_binary(bin) and byte_size(bin) > @max_size, do: {:error, :too_large, 0} + + def pass(bin) when is_binary(bin) do + total = byte_size(bin) + + try do + case item(bin, 0, 0) do + {value, <<>>, _count} -> {:ok, value} + {_value, rest, _count} -> {:error, :trailing_bytes, total - byte_size(rest)} + end + catch + {:n6, code, rest} -> {:error, code, total - byte_size(rest)} + end + end + + # `depth` is the number of containers around this item; `count` the items read so far. + defp item(<<>> = here, _depth, _count), do: fail(:truncated, here) + + defp item(<> = here, depth, count) do + major = ib >>> 5 + ai = ib &&& 0x1F + + cond do + ai in 28..30 -> fail(:not_well_formed, here) + ai == 31 and major in 2..5 -> fail(:indefinite_length, here) + ai == 31 -> fail(:not_well_formed, here) + major == 6 -> fail(:tag_forbidden, here) + major == 7 -> simple(ai, tail, here, count) + true -> argument_item(major, ai, tail, here, depth, count) + end + end + + defp simple(20, tail, here, count), do: {false, tail, counted(count, here)} + defp simple(21, tail, here, count), do: {true, tail, counted(count, here)} + defp simple(22, tail, here, count), do: {nil, tail, counted(count, here)} + defp simple(ai, _tail, here, _count) when ai in 25..27, do: fail(:float_forbidden, here) + defp simple(24, <<>>, here, _count), do: fail(:truncated, here) + defp simple(24, <>, here, _count) when v < 32, do: fail(:not_well_formed, here) + defp simple(_ai, _tail, here, _count), do: fail(:simple_forbidden, here) + + defp argument_item(major, ai, tail, here, depth, count) do + {arg, rest} = argument(ai, tail, here) + count = counted(count, here) + + case major do + 0 -> {arg, rest, count} + 1 when arg > @nint_arg_max -> fail(:int_out_of_range, here) + 1 -> {-1 - arg, rest, count} + 2 -> string(:bytes, arg, rest, here, count) + 3 -> string(:text, arg, rest, here, count) + 4 -> array(arg, rest, here, depth + 1, count) + 5 -> map(arg, rest, here, depth + 1, count) + end + end + + defp counted(count, here) when count + 1 > @max_items, do: fail(:too_many_items, here) + defp counted(count, _here), do: count + 1 + + defp argument(ai, tail, _here) when ai < 24, do: {ai, tail} + defp argument(24, <>, here), do: shortest(v, 24, rest, here) + defp argument(25, <>, here), do: shortest(v, 0x100, rest, here) + defp argument(26, <>, here), do: shortest(v, 0x1_0000, rest, here) + defp argument(27, <>, here), do: shortest(v, 0x1_0000_0000, rest, here) + defp argument(_ai, _tail, here), do: fail(:truncated, here) + + defp shortest(v, min, _rest, here) when v < min, do: fail(:noncanonical, here) + defp shortest(v, _min, rest, _here), do: {v, rest} + + defp string(_kind, len, rest, here, _count) when byte_size(rest) < len, + do: fail(:truncated, here) + + defp string(:bytes, len, rest, _here, count) do + <> = rest + {{:bytes, b}, after_b, count} + end + + defp string(:text, len, _rest, here, _count) when len > @max_text, + do: fail(:text_too_long, here) + + defp string(:text, len, rest, here, count) do + <> = rest + + case text_defect(s) do + nil -> {{:text, s}, after_s, count} + code -> fail(code, here) + end + end + + # Every item takes at least one byte, so a count beyond what is left is refused before + # anything is read: a forged count cannot make the decoder loop. + defp array(_n, _rest, here, depth, _count) when depth > @max_depth, do: fail(:too_deep, here) + defp array(n, rest, here, _depth, _count) when n > byte_size(rest), do: fail(:truncated, here) + defp array(n, rest, _here, depth, count), do: elements(n, rest, depth, count, []) + + defp elements(0, rest, _depth, count, acc), do: {Enum.reverse(acc), rest, count} + + defp elements(n, rest, depth, count, acc) do + {value, rest, count} = item(rest, depth, count) + elements(n - 1, rest, depth, count, [value | acc]) + end + + defp map(_n, _rest, here, depth, _count) when depth > @max_depth, do: fail(:too_deep, here) + defp map(n, rest, here, _depth, _count) when 2 * n > byte_size(rest), do: fail(:truncated, here) + defp map(n, rest, _here, depth, count), do: pairs(n, rest, depth, count, nil, []) + + defp pairs(0, rest, _depth, count, _prev, acc), do: {Map.new(acc), rest, count} + + defp pairs(n, rest, depth, count, prev, acc) do + case rest do + <> when kb >>> 5 != 0 -> fail(:key_not_uint, rest) + _ -> :ok + end + + {key, after_key, count} = item(rest, depth, count) + if key > @key_max, do: fail(:key_not_uint, rest) + key_bytes = binary_part(rest, 0, byte_size(rest) - byte_size(after_key)) + + cond do + prev == nil or key_bytes > prev -> :ok + key_bytes == prev -> fail(:duplicate_key, rest) + true -> fail(:noncanonical, rest) + end + + {value, after_value, count} = item(after_key, depth, count) + pairs(n - 1, after_value, depth, count, key_bytes, [{key, value} | acc]) + end + + @spec fail(Profile.code(), binary()) :: no_return() + defp fail(code, at), do: throw({:n6, code, at}) +end diff --git a/lib/trinity/n6/cose.ex b/lib/trinity/n6/cose.ex new file mode 100644 index 0000000..543fdec --- /dev/null +++ b/lib/trinity/n6/cose.ex @@ -0,0 +1,293 @@ +# SPDX-FileCopyrightText: Sudo Apt Holdings LLC +# SPDX-License-Identifier: Apache-2.0 +defmodule Trinity.N6.COSE do + @moduledoc """ + COSE_Sign1 under the N6 profile (its section 5; slice 138): ML-DSA-87 only, the header rules, + the key thumbprint, signing and verifying. + + ## The protected header + + {1: -50, 4: kid, 16: typ, ? 15: {1: iss, 2: sub}} + + `alg` -50 is ML-DSA-87 (RFC 9964), pure, with an empty context string (RFC 9964 section 5). + `kid` is the RFC 9679 COSE Key Thumbprint, SHA-384, over the AKP members RFC 9964 section 6 + names: `{1: 7, 3: -50, -1: pub}`, encoded under RFC 8949 section 4.2.1, which for a 2,592-byte key + is exactly `a3 01 07 03 38 31 20 59 0a 20` followed by the key. The thumbprint includes `alg`, so a + key cannot be used with another algorithm. `typ` (RFC 9596) is a media type, compared byte for + byte against the table the caller passes; CWT claims (label 15) are present exactly when the + table marks the `typ` as SCITT-renderable. + + The protected label set is closed to {1, 4, 15, 16}: content type (3) is refused with its own + code, and every other label, `crit` (2) and the hash-envelope labels (258 to 260) among them, + with `header_label_forbidden`. The header is strictly decoded (`Trinity.N6.CBOR`); a key that is + not an unsigned integer is `header_label_forbidden` and every other encoding defect + `header_noncanonical` (this tree's reading of the profile's W7). + + ## What is signed + + `ToBeSigned` is RFC 9052 section 4.4's `["Signature1", protected, h'', payload]`, with the + protected bytes exactly as built or received and external_aad always empty. RFC 9052 signs the + full payload "independent of how it is transported", so the signature does not depend on + whether the payload travels attached or detached, or on tag 18. + + A COSE_Sign1 is held as its three parts (`t:parts/0`). `render/2` gives either exchange form: + `:detached`, untagged with a nil payload, which is how the profile's carriers hold one, or + `:attached_tagged`, `#6.18` with the payload inside. Which form a single-signer statement + travels in is Open in the profile (its section 12.1 item 1), so neither is called the profile's. + + ## Verification outcomes + + `verify/2` answers the profile's exit classes: 0 with `:claim`, 1 (invalid) with the code of the + rule that failed, or 5 (no trust) when the key is unknown (`policy_stale_or_unknown_key`) or the + runtime cannot run ML-DSA-87 at all (`alg_unavailable`, this tree's code). A runtime that refuses + the algorithm is never a crash and never a pass. The attribution classes 6 and 7 need the + profile's key descriptors and are not decided here. + + ## Keys + + The private key is the 32-byte seed RFC 9964 section 4 defines, as `{:seed, seed}`, or the + expanded key OTP's `generate_key/2` returns. The public key is passed beside it: on OTP 28.5.0.8 + no form of `:crypto.generate_key/3` derives it from a seed. + """ + + alias Trinity.N6.{CBOR, Profile} + + @alg -50 + @pub_size 2592 + @sig_size 4627 + @thumbprint_prefix <<0xA3, 0x01, 0x07, 0x03, 0x38, 0x31, 0x20, 0x59, 0x0A, 0x20>> + @labels [1, 4, 15, 16] + @required [1, 4, 16] + @countersignature [7, 9, 11, 12] + @tag18 0xD2 + + @typedoc "One COSE_Sign1: the protected header bytes, the payload, the signature." + @type parts :: %{protected: binary(), payload: binary(), signature: binary()} + + @typedoc "The `typ` values an entry point accepts, each saying whether it is SCITT-renderable." + @type typ_table :: %{optional(String.t()) => %{scitt: boolean()}} + + @typedoc "An ML-DSA-87 key: the public key and the private key as a seed or expanded." + @type key :: %{public: binary(), private: {:seed, binary()} | binary()} + + @doc "The RFC 9679 SHA-384 thumbprint of an ML-DSA-87 public key: its `kid`." + @spec thumbprint(binary()) :: binary() + def thumbprint(pub) when byte_size(pub) == @pub_size, + do: :crypto.hash(:sha384, @thumbprint_prefix <> pub) + + @doc "The protected header bytes for a `kid` and `typ`, with CWT `{iss, sub}` when given." + @spec protected_header(binary(), String.t(), {String.t(), String.t()} | nil) :: binary() + def protected_header(kid, typ, cwt \\ nil) when byte_size(kid) == 48 and is_binary(typ) do + header = %{1 => @alg, 4 => {:bytes, kid}, 16 => {:text, typ}} + + header = + case cwt do + {iss, sub} -> Map.put(header, 15, %{1 => {:text, iss}, 2 => {:text, sub}}) + nil -> header + end + + CBOR.encode!(header) + end + + @doc "Strictly decodes protected header bytes under the header rules and a `typ` table." + @spec decode_protected(binary(), typ_table()) :: + {:ok, %{kid: binary(), typ: String.t(), cwt: map() | nil}} | {:error, Profile.code()} + def decode_protected(bytes, table) when is_binary(bytes) and is_map(table) do + with {:ok, header} <- decode_header_map(bytes), + :ok <- labels(header), + :ok <- present(header), + :ok <- alg(header), + {:ok, kid} <- kid(header), + {:ok, typ, entry} <- typ(header, table), + {:ok, cwt} <- cwt(header, entry) do + {:ok, %{kid: kid, typ: typ, cwt: cwt}} + end + end + + defp decode_header_map(bytes) do + case CBOR.decode(bytes) do + {:ok, header} when is_map(header) -> {:ok, header} + {:ok, _other} -> {:error, :header_noncanonical} + {:error, :key_not_uint, _} -> {:error, :header_label_forbidden} + {:error, _code, _} -> {:error, :header_noncanonical} + end + end + + defp labels(header) do + case header |> Map.keys() |> Enum.sort() |> Enum.find(&(&1 not in @labels)) do + nil -> :ok + 3 -> {:error, :content_type_forbidden} + _label -> {:error, :header_label_forbidden} + end + end + + defp present(header) do + if Enum.all?(@required, &is_map_key(header, &1)), + do: :ok, + else: {:error, :header_not_protected} + end + + defp alg(%{1 => @alg}), do: :ok + defp alg(_header), do: {:error, :alg_not_permitted} + + defp kid(%{4 => {:bytes, kid}}) when byte_size(kid) == 48, do: {:ok, kid} + defp kid(_header), do: {:error, :kid_not_thumbprint} + + defp typ(%{16 => {:text, typ}}, table) do + case Map.fetch(table, typ) do + {:ok, entry} -> {:ok, typ, entry} + :error -> {:error, :typ_unexpected} + end + end + + defp typ(_header, _table), do: {:error, :typ_unexpected} + + defp cwt(header, %{scitt: true}) do + case Map.fetch(header, 15) do + {:ok, %{1 => {:text, iss}, 2 => {:text, sub}} = claims} when map_size(claims) == 2 -> + {:ok, %{iss: iss, sub: sub}} + + {:ok, _other} -> + {:error, :header_label_forbidden} + + :error -> + {:error, :header_not_protected} + end + end + + defp cwt(header, _entry) do + if is_map_key(header, 15), do: {:error, :header_label_forbidden}, else: {:ok, nil} + end + + @doc "RFC 9052 section 4.4's ToBeSigned for a Sign1, external_aad empty." + @spec to_be_signed(binary(), binary()) :: binary() + def to_be_signed(protected, payload) when is_binary(protected) and is_binary(payload), + do: + CBOR.encode!([{:text, "Signature1"}, {:bytes, protected}, {:bytes, ""}, {:bytes, payload}]) + + @doc """ + Signs a payload as a Sign1 with ML-DSA-87. `opts`: `cwt: {iss, sub}` for a SCITT-renderable + `typ`. Answers `{:error, :alg_unavailable}` where the runtime cannot sign ML-DSA-87. + """ + @spec sign(binary(), String.t(), key(), keyword()) :: {:ok, parts()} | {:error, Profile.code()} + def sign(payload, typ, %{public: pub, private: priv}, opts \\ []) + when is_binary(payload) and byte_size(pub) == @pub_size and + ((is_binary(priv) and byte_size(priv) == 4896) or + (is_tuple(priv) and tuple_size(priv) == 2 and elem(priv, 0) == :seed and + byte_size(elem(priv, 1)) == 32)) do + protected = protected_header(thumbprint(pub), typ, Keyword.get(opts, :cwt)) + signature = :crypto.sign(:mldsa87, :none, to_be_signed(protected, payload), priv) + {:ok, %{protected: protected, payload: payload, signature: signature}} + rescue + e in ErlangError -> unavailable(e) + end + + @doc """ + Verifies a Sign1's parts. `opts`: `keys`, a function from a `kid` to `{:ok, public_key}` or + `:error`; `typ`, the `typ` table of the entry point in use. + """ + @spec verify(parts(), keyword()) :: + {:ok, %{exit: 0, verdict_code: :claim, kid: binary(), typ: String.t()}} + | {:error, 1 | 5, Profile.code()} + def verify(%{protected: protected, payload: payload, signature: signature}, opts) + when is_binary(protected) and is_binary(payload) and is_binary(signature) do + keys = Keyword.fetch!(opts, :keys) + table = Keyword.fetch!(opts, :typ) + + with {:ok, %{kid: kid, typ: typ}} <- decode_protected(protected, table), + {:ok, pub} <- known_key(keys.(kid)), + :ok <- thumbprint_matches(pub, kid), + :ok <- signature_size(signature), + :ok <- signature_valid(to_be_signed(protected, payload), signature, pub) do + {:ok, %{exit: 0, verdict_code: :claim, kid: kid, typ: typ}} + else + {:error, code} -> {:error, Profile.exit_of(code), code} + end + end + + defp known_key({:ok, pub}) when is_binary(pub), do: {:ok, pub} + defp known_key(_unknown), do: {:error, :policy_stale_or_unknown_key} + + defp thumbprint_matches(pub, kid) when byte_size(pub) == @pub_size do + if thumbprint(pub) == kid, do: :ok, else: {:error, :kid_not_thumbprint} + end + + defp thumbprint_matches(_pub, _kid), do: {:error, :kid_not_thumbprint} + + defp signature_size(sig) when byte_size(sig) == @sig_size, do: :ok + defp signature_size(_sig), do: {:error, :carrier_malformed} + + defp signature_valid(tbs, sig, pub) do + if :crypto.verify(:mldsa87, :none, tbs, sig, pub), do: :ok, else: {:error, :signature_invalid} + rescue + e in ErlangError -> unavailable(e) + end + + # :crypto raises :notsup in FIPS mode on a provider that refuses ML-DSA-87, and badarg where + # the linked library has no ML-DSA at all. Either way this runtime cannot judge. + defp unavailable(%ErlangError{original: {reason, _, _}}) when reason in [:notsup, :badarg], + do: {:error, :alg_unavailable} + + defp unavailable(e), do: raise(e) + + @doc "The bytes of one exchange form of a Sign1." + @spec render(parts(), :detached | :attached_tagged) :: binary() + def render(%{protected: p, signature: s}, :detached), + do: CBOR.encode!([{:bytes, p}, %{}, nil, {:bytes, s}]) + + def render(%{protected: p, payload: payload, signature: s}, :attached_tagged), + do: <<@tag18>> <> CBOR.encode!([{:bytes, p}, %{}, {:bytes, payload}, {:bytes, s}]) + + @doc """ + The parts of a Sign1 in one exchange form. A detached Sign1 takes its payload separately. The + unprotected header must be empty: `alg`, `kid` or `typ` there is `header_not_protected`, a + countersignature label (7, 9, 11, 12) `countersignature_forbidden`, any other label + `unprotected_forbidden`. + """ + @spec parse(binary(), :detached, binary()) :: {:ok, parts()} | {:error, Profile.code()} + @spec parse(binary(), :attached_tagged) :: {:ok, parts()} | {:error, Profile.code()} + def parse(bytes, :detached, payload) when is_binary(bytes) and is_binary(payload) do + with {:ok, [{:bytes, p}, unprotected, nil, {:bytes, s}]} <- sign1(bytes), + :ok <- unprotected(unprotected) do + {:ok, %{protected: p, payload: payload, signature: s}} + else + {:ok, _other} -> {:error, :carrier_malformed} + {:error, code} -> {:error, code} + end + end + + def parse(<<@tag18, bytes::binary>>, :attached_tagged) do + with {:ok, [{:bytes, p}, unprotected, {:bytes, payload}, {:bytes, s}]} <- sign1(bytes), + :ok <- unprotected(unprotected) do + {:ok, %{protected: p, payload: payload, signature: s}} + else + {:ok, _other} -> {:error, :carrier_malformed} + {:error, code} -> {:error, code} + end + end + + def parse(bytes, :attached_tagged) when is_binary(bytes), do: {:error, :carrier_malformed} + + # The only map in a Sign1 is the unprotected header, so a key the codec refuses is one there. + defp sign1(bytes) do + case CBOR.decode(bytes) do + {:ok, value} -> {:ok, value} + {:error, :key_not_uint, _} -> {:error, :unprotected_forbidden} + {:error, _code, _} -> {:error, :carrier_malformed} + end + end + + defp unprotected(map) when map == %{}, do: :ok + + defp unprotected(map) when is_map(map) do + keys = Map.keys(map) + + cond do + Enum.any?(keys, &(&1 in @required)) -> {:error, :header_not_protected} + Enum.any?(keys, &(&1 in @countersignature)) -> {:error, :countersignature_forbidden} + true -> {:error, :unprotected_forbidden} + end + end + + defp unprotected(_other), do: {:error, :carrier_malformed} +end diff --git a/lib/trinity/n6/digest.ex b/lib/trinity/n6/digest.ex new file mode 100644 index 0000000..dfe488c --- /dev/null +++ b/lib/trinity/n6/digest.ex @@ -0,0 +1,51 @@ +# SPDX-FileCopyrightText: Sudo Apt Holdings LLC +# SPDX-License-Identifier: Apache-2.0 +defmodule Trinity.N6.Digest do + @moduledoc """ + Digests of foreign bytes inside N6 bodies (the profile's section 4; slice 138). + + Bytes someone else produced are never parsed, normalised or re-encoded before they are hashed. + They are referenced by a digest over exactly the bytes as received: + + * `Digest = [hashAlg, hashValue]`, the shape of RFC 9054's `COSE_Hash_Find` example, with + `hashAlg` -43 (SHA-384, RFC 9054) and a 48-byte value; + * `ForeignRef = {0: Digest, 1: length, 2: media type, ? 3: locator}`, the length being the + exact number of bytes received. + + SHA-384 is the only algorithm accepted. The profile leaves SHA-512 (-44) Open for v0; until it + is decided this tree refuses it with the rest (`digest_alg_not_permitted`). A value whose length + does not match its algorithm is `digest_length_mismatch`, and anything that is not a two-element + array of an integer and a byte string is `digest_malformed`; both are this tree's codes until + the profile names its own. + """ + + alias Trinity.N6.{CBOR, Profile} + + @sha384 -43 + + @doc "The Digest of bytes exactly as given." + @spec of(binary()) :: [integer() | {:bytes, binary()}] + def of(bytes) when is_binary(bytes), do: [@sha384, {:bytes, :crypto.hash(:sha384, bytes)}] + + @doc "A ForeignRef to bytes exactly as given, with their media type and an optional locator." + @spec foreign_ref(binary(), String.t(), String.t() | nil) :: %{ + non_neg_integer() => CBOR.value() + } + def foreign_ref(bytes, media_type, locator \\ nil) + when is_binary(bytes) and is_binary(media_type) do + ref = %{0 => of(bytes), 1 => byte_size(bytes), 2 => {:text, media_type}} + if locator, do: Map.put(ref, 3, {:text, locator}), else: ref + end + + @doc "Whether a decoded value is a Digest this profile accepts." + @spec check(CBOR.value()) :: :ok | {:error, Profile.code()} + def check([@sha384, {:bytes, value}]) when byte_size(value) == 48, do: :ok + def check([@sha384, {:bytes, _value}]), do: {:error, :digest_length_mismatch} + def check([alg, {:bytes, _value}]) when is_integer(alg), do: {:error, :digest_alg_not_permitted} + def check(_other), do: {:error, :digest_malformed} + + @doc "Whether a Digest this profile accepts is the digest of these bytes." + @spec matches?(CBOR.value(), binary()) :: boolean() + def matches?(digest, bytes) when is_binary(bytes), + do: check(digest) == :ok and digest == of(bytes) +end diff --git a/lib/trinity/n6/profile.ex b/lib/trinity/n6/profile.ex new file mode 100644 index 0000000..a5a8fa4 --- /dev/null +++ b/lib/trinity/n6/profile.ex @@ -0,0 +1,129 @@ +# SPDX-FileCopyrightText: Sudo Apt Holdings LLC +# SPDX-License-Identifier: Apache-2.0 +defmodule Trinity.N6.Profile do + @moduledoc """ + The N6 profile this tree implements, pinned: its version, its limits, and the closed table of + refusal codes with the exit class of each (slice 138). + + N6 is a profile of deterministic CBOR (RFC 8949 section 4.2.1) and COSE_Sign1 (RFC 9052) for + signed statements. The version implemented here is **v0, draft r3, provisional**: every rule may + change until a frozen v1 is published with its vectors. `pin/0` names the draft and the SHA-384 + of its text, so a later draft is a visible change of pin, not a silent one. + + ## Codes + + Every refusal in `Trinity.N6` is one of the atoms in `codes/0`. Each carries its exit class + (0 verified, 1 invalid, 2 usage, 5 no trust) and whether the draft defines it (`:draft`) or this + tree does, where the draft leaves the code Open (`:trinity`). A `:trinity` code is provisional + twice over: it changes when the draft settles that point. + + ## Limits + + The draft's numbers, Open in it (its section 12.1 item 2), counted as follows, which the draft + also leaves Open (its VB2) and so is this tree's provisional reading: + + * **depth** is the number of arrays and maps around an item, the top-level container being 1: + 16 nested arrays are accepted, 17 are refused; + * **items** counts every data item, map keys and the top-level item included; + * **text** is the length of one text string in UTF-8 bytes; + * **size** is the whole input in bytes. There is no separate limit on byte strings. + + Each is accepted at its value and refused one past it. Limits apply to bytes being decoded, not + to bytes this tree constructs: a ToBeSigned may be larger than any body it carries. + """ + + @pin %{ + version: "n6/v0", + draft: "r3", + status: :provisional, + draft_sha384: + "54ab5e549259b87b18c51b4d1a3dbbbbb230b74ae562d5c2ef8ec43288c6c2dafc6376bff51ae205da6043bb9da7b7f2" + } + + @limits %{depth: 16, items: 65_536, text: 65_536, size: 1_048_576} + + # code => {exit class, who defines it} + @codes %{ + # Section 2, the encoding rules. + noncanonical: {1, :draft}, + indefinite_length: {1, :draft}, + key_not_uint: {1, :draft}, + duplicate_key: {1, :draft}, + int_out_of_range: {1, :draft}, + float_forbidden: {1, :draft}, + tag_forbidden: {1, :draft}, + simple_forbidden: {1, :draft}, + invalid_utf8: {1, :draft}, + not_nfc: {1, :draft}, + too_deep: {1, :draft}, + too_large: {1, :draft}, + too_many_items: {1, :draft}, + text_too_long: {1, :draft}, + trailing_bytes: {1, :draft}, + truncated: {1, :draft}, + # A head the draft's code table does not cover yet (its W6): additional information 28 to 30, + # 31 on a major type that has no indefinite form, a lone break, 0xf8 below 32. + not_well_formed: {1, :trinity}, + # A term the encoder cannot express in the subset (an atom, a bare binary, a tuple). + value_not_n6: {1, :trinity}, + # Section 3, the body rules. + schema_version_missing: {1, :draft}, + unknown_schema: {1, :draft}, + unknown_key: {1, :draft}, + schema_reserved: {1, :draft}, + set_not_canonical: {1, :draft}, + body_not_map: {1, :trinity}, + required_key_missing: {1, :trinity}, + wrong_type: {1, :trinity}, + # Section 4, digests. + digest_alg_not_permitted: {1, :draft}, + # The draft's own name for it (its W29), not yet in its code list. + digest_length_mismatch: {1, :trinity}, + digest_malformed: {1, :trinity}, + # Section 5, COSE_Sign1. + alg_not_permitted: {1, :draft}, + header_not_protected: {1, :draft}, + header_label_forbidden: {1, :draft}, + header_noncanonical: {1, :draft}, + content_type_forbidden: {1, :draft}, + countersignature_forbidden: {1, :draft}, + unprotected_forbidden: {1, :draft}, + kid_not_thumbprint: {1, :draft}, + typ_unexpected: {1, :draft}, + carrier_malformed: {1, :draft}, + # The draft names signature failures per twin role (signature_invalid_kv, _kh); a + # single-signer statement has no role yet (its section 12.1 item 1). + signature_invalid: {1, :trinity}, + # Section 9. + claim: {0, :draft}, + usage: {2, :draft}, + policy_stale_or_unknown_key: {5, :draft}, + # The runtime cannot run ML-DSA-87 (for example in FIPS mode on a provider that refuses it), + # so the verifier cannot judge: no trust, never a pass and never a crash. + alg_unavailable: {5, :trinity} + } + + @typedoc "A refusal code, or `:claim` for a full verdict." + @type code :: atom() + + @doc "The draft this tree implements and the SHA-384 of its posted text." + @spec pin() :: map() + def pin, do: @pin + + @doc "The four limits, counted as the moduledoc says." + @spec limits() :: %{ + depth: pos_integer(), + items: pos_integer(), + text: pos_integer(), + size: pos_integer() + } + def limits, do: @limits + + @doc "Every code, with its exit class and whether the draft defines it." + @spec codes() :: %{code() => {0 | 1 | 2 | 5, :draft | :trinity}} + def codes, do: @codes + + @doc "The exit class of a code. Raises on a code not in the table, which is a bug in the caller." + @spec exit_of(code()) :: 0 | 1 | 2 | 5 + def exit_of(code), do: @codes |> Map.fetch!(code) |> elem(0) +end diff --git a/scripts/n6_vectors.exs b/scripts/n6_vectors.exs new file mode 100644 index 0000000..5ab3ffd --- /dev/null +++ b/scripts/n6_vectors.exs @@ -0,0 +1,454 @@ +# SPDX-FileCopyrightText: Sudo Apt Holdings LLC +# SPDX-License-Identifier: Apache-2.0 +# +# Writes Trinity's provisional N6 vectors (slice 138) into the directory given, in the layout of the +# N6 profile v0 draft r3, section 10: +# +# elixir scripts/n6_vectors.exs test/fixtures/n6 +# +# Every expected encoding below is a hex literal, either copied from RFC 8949 Appendix A or built by +# hand from CBOR heads, so that no vector is the encoder's opinion of itself: the encoder under test +# (`Trinity.N6.CBOR`) is not loaded here. This script only turns each literal into the files the +# layout asks for (the description, the diagnostic notation, the expected outcome) and writes +# SHA384SUMS in the form `sha384sum -c` reads. Running it twice writes the same bytes. +# +# The limit vectors at 64 KiB and 1 MiB are not checked in; the suite builds them in memory by the +# rule written in Trinity.N6.Profile. + +defmodule N6Vectors do + @typ "application/vnd.example.test.v1+cose" + @typ_scitt "application/vnd.example.scitt-test.v1+cose" + @typ_other "application/vnd.example.other.v1+cose" + + # FIPS 180-2, Appendix D.1: SHA-384("abc"). + @sha384_abc "cb00753f45a35e8bb5a03d699ac65007272c32ab0eded1631a8b605a43ff5bed8086072ba1e7cc2358baeca134c825a7" + + def run([root]) do + acvp = Path.expand("../priv/kat/acvp/mldsa87_keygen.json", __DIR__) |> File.read!() |> JSON.decode!() + pub = Base.decode16!(acvp["pk"]) + 2592 = byte_size(pub) + + # RFC 9679 section 3 over the AKP members of RFC 9964 section 6: {1: 7, 3: -50, -1: pub}, + # keys in bytewise order 0x01, 0x03, 0x20, a 2,592-byte string's head being 59 0a 20. + thumb_input = h("a3 01 07 03 38 31 20 59 0a 20") <> pub + kid = :crypto.hash(:sha384, thumb_input) + + File.rm_rf!(root) + + for {id, hex, desc, source} <- encoding_positive(), do: positive(root, "encoding", id, h(hex), desc, source) + for {defect, id, hex, code, source} <- encoding_negative(), do: negative(root, "encoding", defect, id, h(hex), code, source) + for {id, hex, desc} <- body_positive(), do: positive(root, "body", id, h(hex), desc, "hand") + for {defect, id, hex, code} <- body_negative(), do: negative(root, "body", defect, id, h(hex), code, "hand") + write(root, "body/schema.json", json(test_schema())) + + header = header_vectors(kid) + for {id, bytes} <- header.positive, do: write(root, "header/positive/#{id}.cbor", bytes) + for {defect, id, bytes, code} <- header.negative, do: negative(root, "header", defect, id, bytes, code, "hand") + write(root, "header/profile-table.json", json(profile_table())) + + write(root, "thumbprint/acvp_mldsa87_tg3_tc51.pub", pub) + write(root, "thumbprint/acvp_mldsa87_tg3_tc51.kid", kid) + + abc = "abc" + digest = :crypto.hash(:sha384, abc) + ^digest = Base.decode16!(@sha384_abc, case: :lower) + + # ForeignRef {0: [-43, digest], 1: 3, 2: "application/octet-stream"} + ref = h("a3 00 82 38 2a 58 30") <> digest <> h("01 03 02 78 18") <> "application/octet-stream" + write(root, "digest/abc.in", abc) + write(root, "digest/abc.ref.cbor", ref) + + for {defect, id, bytes, code} <- digest_negative(), + do: negative(root, "digest", defect, id, bytes, code, "hand") + + write(root, "VERSION", "n6/v0 draft r3 PROVISIONAL (Trinity candidate set)\n") + write(root, "README.md", readme(header.negative)) + write_sums(root) + end + + # The README lists every negative directory with its expected code, derived from the lists above. + defp readme(header_negative) do + rows = fn vectors -> + vectors + |> Enum.map(fn v -> {elem(v, 0), code_of(v)} end) + |> Enum.uniq() + |> Enum.map_join("\n", fn {defect, code} -> "| `#{defect}` | `#{code}` |" end) + end + + """ + # N6 vectors (Trinity candidate set, provisional) + + Written by `scripts/n6_vectors.exs`; do not edit by hand. The layout follows section 10 of the N6 + profile v0, draft r3, which is provisional: every rule may change until v1 is frozen with the + profile's own vectors. These are candidate input, not the published set. + + * Pin: `VERSION`, plus `SHA384SUMS`, which `sha384sum -c SHA384SUMS` checks from this directory + (lines ` `, sorted by path, LF; every file but itself). + * `*.desc.json`: the value, integers as decimal strings (`{"i":"..."}`), byte strings and text as + hex (`{"b":..}`, `{"t":..}`), arrays `{"a":[..]}`, maps `{"m":[[k,v],..]}`, `{"s":"true|false|null"}`, + and the vector's source. + * `*.expect`: `{"exit": 1, "verdict_code": ..., "source": ...}`. The draft leaves this format Open + (its VB5); this is Trinity's. + * Expected encodings are hex literals in the generator, copied from RFC 8949 Appendix A or built + by hand from CBOR heads, never output of the encoder under test. + * `body/schema.json` and `header/profile-table.json` are Trinity's test schema and test `typ` + table (the draft leaves both Open, VB6 and VB4). `thumbprint/` uses NIST ACVP's published + ML-DSA-87 keyGen case (tgId 3, tcId 51); no key of this project's is published. + * Not included: the limit vectors at 64 KiB and 1 MiB (the suite builds them), and COSE_Sign1 + samples (ML-DSA signatures are hedged, so a sample is one valid signature among many; the + suite signs and verifies where the runtime can). + + ## Encoding defects + + | Directory | Expected code | + | -- | -- | + #{rows.(encoding_negative())} + + ## Body defects (under `body/schema.json`) + + | Directory | Expected code | + | -- | -- | + #{rows.(body_negative())} + + ## Header defects (under `header/profile-table.json`) + + | Directory | Expected code | + | -- | -- | + #{rows.(header_negative)} + + ## Digest defects + + | Directory | Expected code | + | -- | -- | + #{rows.(digest_negative())} + """ + end + + # Encoding vectors carry a source after the code; the other lists do not. + defp code_of(v) do + case v do + {_defect, _id, _bytes, code, _source} -> code + {_defect, _id, _bytes, code} -> code + end + end + + ## Encoding: positive + + # {id, hex, description, source} + defp encoding_positive do + a = "RFC 8949 Appendix A" + + [ + {"uint_0", "00", {:i, 0}, a}, + {"uint_1", "01", {:i, 1}, a}, + {"uint_10", "0a", {:i, 10}, a}, + {"uint_23", "17", {:i, 23}, a}, + {"uint_24", "18 18", {:i, 24}, a}, + {"uint_25", "18 19", {:i, 25}, a}, + {"uint_100", "18 64", {:i, 100}, a}, + {"uint_1000", "19 03 e8", {:i, 1000}, a}, + {"uint_1000000", "1a 00 0f 42 40", {:i, 1_000_000}, a}, + {"uint_1000000000000", "1b 00 00 00 e8 d4 a5 10 00", {:i, 1_000_000_000_000}, a}, + {"uint_max_u64", "1b ff ff ff ff ff ff ff ff", {:i, 18_446_744_073_709_551_615}, a}, + {"nint_1", "20", {:i, -1}, a}, + {"nint_10", "29", {:i, -10}, a}, + {"nint_100", "38 63", {:i, -100}, a}, + {"nint_1000", "39 03 e7", {:i, -1000}, a}, + {"nint_min_i64", "3b 7f ff ff ff ff ff ff ff", {:i, -9_223_372_036_854_775_808}, "hand: -2^63, the bottom of E4"}, + {"uint_255", "18 ff", {:i, 255}, "hand: the top of a one-byte argument"}, + {"uint_256", "19 01 00", {:i, 256}, "hand: the bottom of a two-byte argument"}, + {"uint_65535", "19 ff ff", {:i, 65_535}, "hand"}, + {"uint_65536", "1a 00 01 00 00", {:i, 65_536}, "hand"}, + {"uint_4294967295", "1a ff ff ff ff", {:i, 4_294_967_295}, "hand"}, + {"uint_4294967296", "1b 00 00 00 01 00 00 00 00", {:i, 4_294_967_296}, "hand"}, + {"false", "f4", {:s, "false"}, a}, + {"true", "f5", {:s, "true"}, a}, + {"null", "f6", {:s, "null"}, a}, + {"bstr_empty", "40", {:b, ""}, a}, + {"bstr_01020304", "44 01 02 03 04", {:b, <<1, 2, 3, 4>>}, a}, + {"tstr_empty", "60", {:t, ""}, a}, + {"tstr_a", "61 61", {:t, "a"}, a}, + {"tstr_ietf", "64 49 45 54 46", {:t, "IETF"}, a}, + {"tstr_quote_backslash", "62 22 5c", {:t, "\"\\"}, a}, + {"tstr_u_umlaut", "62 c3 bc", {:t, "ü"}, a}, + {"tstr_water", "63 e6 b0 b4", {:t, "æ°´"}, a}, + {"tstr_aegean_10151", "64 f0 90 85 91", {:t, "\u{10151}"}, a}, + {"array_empty", "80", {:a, []}, a}, + {"array_123", "83 01 02 03", {:a, [{:i, 1}, {:i, 2}, {:i, 3}]}, a}, + {"array_nested", "83 01 82 02 03 82 04 05", + {:a, [{:i, 1}, {:a, [{:i, 2}, {:i, 3}]}, {:a, [{:i, 4}, {:i, 5}]}]}, a}, + {"array_25", "98 19 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17 18 18 18 19", + {:a, Enum.map(1..25, &{:i, &1})}, a}, + {"map_empty", "a0", {:m, []}, a}, + {"map_1_2_3_4", "a2 01 02 03 04", {:m, [{{:i, 1}, {:i, 2}}, {{:i, 3}, {:i, 4}}]}, a}, + {"map_keys_across_heads", "a5 0a 00 17 01 18 18 02 18 64 03 19 03 e8 04", + {:m, [{{:i, 10}, {:i, 0}}, {{:i, 23}, {:i, 1}}, {{:i, 24}, {:i, 2}}, {{:i, 100}, {:i, 3}}, {{:i, 1000}, {:i, 4}}]}, + "hand: unsigned keys with 0-, 1- and 2-byte arguments, in bytewise order"}, + {"map_key_max_u32", "a1 1a ff ff ff ff 00", {:m, [{{:i, 4_294_967_295}, {:i, 0}}]}, "hand: the top of E2"}, + {"map_mixed_values", "a4 00 01 01 41 ff 02 61 78 03 82 f5 f6", + {:m, + [ + {{:i, 0}, {:i, 1}}, + {{:i, 1}, {:b, <<0xFF>>}}, + {{:i, 2}, {:t, "x"}}, + {{:i, 3}, {:a, [{:s, "true"}, {:s, "null"}]}} + ]}, "hand"}, + {"depth_16", String.duplicate("81 ", 15) <> "80", nest(16), "hand: the depth limit, counted as Trinity.N6.Profile states"} + ] + end + + defp nest(1), do: {:a, []} + defp nest(n), do: {:a, [nest(n - 1)]} + + ## Encoding: negative + + # {defect, id, hex, code, source} + defp encoding_negative do + a = "RFC 8949 Appendix A" + + [ + {"nonminimal_head", "uint_0_in_one_byte", "18 00", "noncanonical", "hand"}, + {"nonminimal_head", "uint_23_in_one_byte", "18 17", "noncanonical", "hand"}, + {"nonminimal_head", "uint_255_in_two_bytes", "19 00 ff", "noncanonical", "hand"}, + {"nonminimal_head", "length_in_two_bytes", "59 00 01 00", "noncanonical", "hand"}, + {"nonminimal_head", "nested_in_array", "81 18 05", "noncanonical", "hand"}, + {"indefinite_length", "bstr", "5f 42 01 02 43 03 04 05 ff", "indefinite_length", a}, + {"indefinite_length", "tstr", "7f 65 73 74 72 65 61 64 6d 69 6e 67 ff", "indefinite_length", a}, + {"indefinite_length", "array_empty", "9f ff", "indefinite_length", a}, + {"indefinite_length", "map", "bf 01 02 ff", "indefinite_length", "hand"}, + {"indefinite_length", "nested_in_array", "83 01 82 02 03 9f 04 05 ff", "indefinite_length", a}, + {"float_half_1.0", "top", "f9 3c 00", "float_forbidden", a}, + {"float_half_1.0", "zero", "f9 00 00", "float_forbidden", a}, + {"float_half_1.0", "negative_zero", "f9 80 00", "float_forbidden", a}, + {"float64_int_valued", "1e300", "fb 7e 37 e4 3c 88 00 75 9c", "float_forbidden", a}, + {"float64_int_valued", "nested_in_map_value", "a1 01 fb 3f f0 00 00 00 00 00 00", "float_forbidden", "hand: 1.0 as a double"}, + {"float32", "100000", "fa 47 c3 50 00", "float_forbidden", a}, + {"nan", "half", "f9 7e 00", "float_forbidden", a}, + {"nan", "single", "fa 7f c0 00 00", "float_forbidden", a}, + {"nan", "double", "fb 7f f8 00 00 00 00 00 00", "float_forbidden", a}, + {"undefined", "top", "f7", "simple_forbidden", a}, + {"simple_16", "top", "f0", "simple_forbidden", a}, + {"simple_2byte", "simple_255", "f8 ff", "simple_forbidden", a}, + {"simple_2byte", "simple_32", "f8 20", "simple_forbidden", "hand"}, + {"tag_bignum_small", "positive_bignum_1", "c2 41 01", "tag_forbidden", "hand"}, + {"tag_bignum_small", "rfc_2_64", "c2 49 01 00 00 00 00 00 00 00 00", "tag_forbidden", a}, + {"tag_1_datetime", "epoch", "c1 1a 51 4b 67 b0", "tag_forbidden", a}, + {"tag_1_datetime", "nested_in_array", "81 c1 1a 51 4b 67 b0", "tag_forbidden", "hand"}, + {"tag_cose_sign1", "tag_18", "d2 80", "tag_forbidden", "hand: COSE_Sign1's tag inside N6 bytes"}, + {"text_nfd", "e_combining_acute", "63 65 cc 81", "not_nfc", "hand: U+0065 U+0301"}, + {"text_nfd", "nested_in_map_value", "a1 01 63 65 cc 81", "not_nfc", "hand"}, + {"text_bad_utf8", "lone_ff", "62 61 ff", "invalid_utf8", "hand"}, + {"text_bad_utf8", "truncated_sequence", "62 61 c3", "invalid_utf8", "hand"}, + {"text_overlong_utf8", "slash", "62 c0 af", "invalid_utf8", "hand: '/' in two bytes"}, + {"text_surrogate", "d800", "63 ed a0 80", "invalid_utf8", "hand: U+D800 in UTF-8 form"}, + {"text_key", "rfc_map", "a2 61 61 01 61 62 82 02 03", "key_not_uint", a}, + {"text_key", "top", "a1 61 61 01", "key_not_uint", "hand"}, + {"nested_text_key", "in_array", "82 61 61 a1 61 62 61 63", "key_not_uint", a}, + {"negative_map_key", "minus_1", "a1 20 00", "key_not_uint", "hand"}, + {"map_key_over_u32", "two_to_32", "a1 1b 00 00 00 01 00 00 00 00 00", "key_not_uint", "hand"}, + {"bstr_key", "h01", "a1 41 01 00", "key_not_uint", "hand"}, + {"swap_keys", "2_then_1", "a2 02 00 01 00", "noncanonical", "hand"}, + {"swap_keys", "24_then_23", "a2 18 18 00 17 00", "noncanonical", "hand: a longer head after a shorter key"}, + {"duplicate_key", "1_twice", "a2 01 00 01 01", "duplicate_key", "hand"}, + {"duplicate_key", "nested", "81 a2 05 00 05 00", "duplicate_key", "hand"}, + {"int_below_dcbor_range", "minus_2_63_minus_1", "3b 80 00 00 00 00 00 00 00", "int_out_of_range", "hand: -2^63-1"}, + {"int_below_dcbor_range", "rfc_minus_2_64", "3b ff ff ff ff ff ff ff ff", "int_out_of_range", a}, + {"trailing_bytes", "two_items", "00 00", "trailing_bytes", "hand"}, + {"trailing_bytes", "after_array", "80 00", "trailing_bytes", "hand"}, + {"truncated", "head_argument", "19 01", "truncated", "hand"}, + {"truncated", "bstr_body", "44 01 02", "truncated", "hand"}, + {"truncated", "array_items", "83 01 02", "truncated", "hand"}, + {"truncated", "empty_input", "", "truncated", "hand"}, + {"too_deep", "depth_17", String.duplicate("81 ", 16) <> "80", "too_deep", "hand: one past the depth limit"}, + {"not_well_formed", "ai_28", "1c", "not_well_formed", "hand: reserved additional information"}, + {"not_well_formed", "ai_30_nint", "3e", "not_well_formed", "hand"}, + {"not_well_formed", "lone_break", "ff", "not_well_formed", "hand"}, + {"not_well_formed", "uint_ai_31", "1f", "not_well_formed", "hand"}, + {"not_well_formed", "f8_below_32", "f8 10", "not_well_formed", "hand: RFC 8949 section 3.3"} + ] + end + + ## Body, under the published test schema (body/schema.json) + + defp test_schema do + %{ + "provisional" => true, + "note" => + "Trinity's test schema for the body vectors. The draft leaves test schemas Open (VB6); this one is Trinity's.", + "kind" => "trinity.test", + "schema_version" => 1, + "fields" => [ + %{"key" => 16, "name" => "name", "type" => "tstr", "required" => true}, + %{"key" => 17, "name" => "count", "type" => "uint", "required" => false}, + %{"key" => 18, "name" => "members", "type" => "set of uint", "required" => false} + ] + } + end + + defp body_positive do + [ + {"minimal", "a2 00 01 10 61 78", {:m, [{{:i, 0}, {:i, 1}}, {{:i, 16}, {:t, "x"}}]}}, + {"all_fields", "a4 00 01 10 61 78 11 05 12 83 01 02 03", + {:m, + [ + {{:i, 0}, {:i, 1}}, + {{:i, 16}, {:t, "x"}}, + {{:i, 17}, {:i, 5}}, + {{:i, 18}, {:a, [{:i, 1}, {:i, 2}, {:i, 3}]}} + ]}}, + {"set_sorted_by_encoded_bytes", "a3 00 01 10 61 78 12 83 17 18 18 19 01 00", + {:m, + [ + {{:i, 0}, {:i, 1}}, + {{:i, 16}, {:t, "x"}}, + {{:i, 18}, {:a, [{:i, 23}, {:i, 24}, {:i, 256}]}} + ]}} + ] + end + + # {defect, id, hex, code} + defp body_negative do + [ + {"no_schema_version", "no_key_0", "a1 10 61 78", "schema_version_missing"}, + {"bool_schema_version", "true", "a2 00 f5 10 61 78", "key_not_uint"}, + {"negative_schema_version", "minus_1", "a2 00 20 10 61 78", "key_not_uint"}, + {"carrier_schema_key0", "envelope_schema", "a2 00 1a ff ff 00 01 10 61 78", "schema_reserved"}, + {"carrier_schema_key0", "top_of_range", "a2 00 1a ff ff ff ff 10 61 78", "schema_reserved"}, + {"unknown_schema", "version_2", "a2 00 02 10 61 78", "unknown_schema"}, + {"unknown_key", "key_19", "a3 00 01 10 61 78 13 00", "unknown_key"}, + {"required_key_missing", "no_name", "a1 00 01", "required_key_missing"}, + {"wrong_type", "name_as_uint", "a2 00 01 10 01", "wrong_type"}, + {"set_unsorted", "2_then_1", "a3 00 01 10 61 78 12 82 02 01", "set_not_canonical"}, + {"set_unsorted", "longer_head_first", "a3 00 01 10 61 78 12 82 18 18 17", "set_not_canonical"}, + {"set_duplicate", "1_twice", "a3 00 01 10 61 78 12 82 01 01", "set_not_canonical"}, + {"not_a_map", "array", "82 00 01", "body_not_map"} + ] + end + + ## Header (decoded as a protected header, not as a body) + + defp profile_table do + %{ + "provisional" => true, + "note" => "Trinity's test profile table for the header vectors (the draft leaves its CDDL Open, VB4).", + "typ" => [ + %{"typ" => @typ, "carrier" => "single", "signers" => "single", "scitt" => false}, + %{"typ" => @typ_scitt, "carrier" => "single", "signers" => "single", "scitt" => true} + ] + } + end + + defp header_vectors(kid) do + alg = h("01 38 31") + kid_pair = h("04 58 30") <> kid + typ = h("10 78 24") <> @typ + typ_scitt = h("10 78 2a") <> @typ_scitt + typ_other = h("10 78 25") <> @typ_other + cwt = h("0f a2 01 63") <> "iss" <> h("02 63") <> "sub" + + %{ + positive: [ + {"alg_kid_typ", h("a3") <> alg <> kid_pair <> typ}, + {"alg_kid_cwt_typ_scitt", h("a4") <> alg <> kid_pair <> cwt <> typ_scitt} + ], + negative: [ + {"alg_not_mldsa87", "mldsa65", h("a3 01 38 30") <> kid_pair <> typ, "alg_not_permitted"}, + {"alg_not_mldsa87", "eddsa", h("a3 01 27") <> kid_pair <> typ, "alg_not_permitted"}, + {"content_type", "label_3", h("a4") <> alg <> h("03 61 78") <> kid_pair <> typ, "content_type_forbidden"}, + {"crit", "label_2", h("a4") <> alg <> h("02 81 01") <> kid_pair <> typ, "header_label_forbidden"}, + {"label_0", "label_0", h("a4 00 00") <> alg <> kid_pair <> typ, "header_label_forbidden"}, + {"label_5", "iv", h("a4") <> alg <> kid_pair <> h("05 41 00") <> typ, "header_label_forbidden"}, + {"label_negative", "minus_1", h("a4") <> alg <> kid_pair <> typ <> h("20 00"), "header_label_forbidden"}, + {"label_hash_envelope", "label_258", h("a4") <> alg <> kid_pair <> typ <> h("19 01 02 38 2a"), "header_label_forbidden"}, + {"alg_missing", "no_label_1", h("a2") <> kid_pair <> typ, "header_not_protected"}, + {"kid_missing", "no_label_4", h("a2") <> alg <> typ, "header_not_protected"}, + {"typ_missing", "no_label_16", h("a2") <> alg <> kid_pair, "header_not_protected"}, + {"typ_uint", "typ_1", h("a3") <> alg <> kid_pair <> h("10 01"), "typ_unexpected"}, + {"typ_not_in_table", "other", h("a3") <> alg <> kid_pair <> typ_other, "typ_unexpected"}, + {"cwt_without_scitt_typ", "cwt_on_plain_typ", h("a4") <> alg <> kid_pair <> cwt <> typ, "header_label_forbidden"}, + {"kid_wrong_size", "kid_32", h("a3") <> alg <> h("04 58 20") <> binary_part(kid, 0, 32) <> typ, "kid_not_thumbprint"}, + {"header_noncanonical", "typ_before_kid", h("a3") <> alg <> typ <> kid_pair, "header_noncanonical"}, + {"header_noncanonical", "nonminimal_label", h("a3 18 01 38 31") <> kid_pair <> typ, "header_noncanonical"}, + {"header_noncanonical", "trailing_byte", h("a3") <> alg <> kid_pair <> typ <> h("00"), "header_noncanonical"} + ] + } + end + + ## Digest + + defp digest_negative do + d32 = :binary.copy(<<0xAB>>, 32) + d48 = :binary.copy(<<0xAB>>, 48) + + [ + {"digest_alg_not_permitted", "sha256", h("82 2f 58 20") <> d32, "digest_alg_not_permitted"}, + {"digest_alg_not_permitted", "shake256", h("82 38 2c 58 40") <> :binary.copy(<<0xAB>>, 64), "digest_alg_not_permitted"}, + {"digest_length_mismatch", "sha384_32_bytes", h("82 38 2a 58 20") <> d32, "digest_length_mismatch"}, + {"digest_not_array", "bstr", h("58 30") <> d48, "digest_malformed"} + ] + end + + ## Files + + defp positive(root, layer, id, bytes, desc, source) do + write(root, "#{layer}/positive/#{id}.cbor", bytes) + write(root, "#{layer}/positive/#{id}.desc.json", json(%{"value" => desc_json(desc), "source" => source})) + write(root, "#{layer}/positive/#{id}.diag", diag(desc) <> "\n") + end + + defp negative(root, layer, defect, id, bytes, code, source) do + write(root, "#{layer}/negative/#{defect}/#{id}.cbor", bytes) + + write( + root, + "#{layer}/negative/#{defect}/#{id}.expect", + json(%{"exit" => 1, "verdict_code" => code, "source" => source}) + ) + end + + defp desc_json({:i, n}), do: %{"i" => Integer.to_string(n)} + defp desc_json({:b, b}), do: %{"b" => Base.encode16(b, case: :lower)} + defp desc_json({:t, t}), do: %{"t" => Base.encode16(t, case: :lower)} + defp desc_json({:s, s}), do: %{"s" => s} + defp desc_json({:a, items}), do: %{"a" => Enum.map(items, &desc_json/1)} + defp desc_json({:m, pairs}), do: %{"m" => Enum.map(pairs, fn {k, v} -> [desc_json(k), desc_json(v)] end)} + + # RFC 8949 section 8, for the subset: integers in decimal, h'..' byte strings, JSON-style text. + defp diag({:i, n}), do: Integer.to_string(n) + defp diag({:b, b}), do: "h'" <> Base.encode16(b, case: :lower) <> "'" + defp diag({:t, t}), do: JSON.encode!(t) + defp diag({:s, s}), do: s + defp diag({:a, items}), do: "[" <> Enum.map_join(items, ", ", &diag/1) <> "]" + defp diag({:m, pairs}), do: "{" <> Enum.map_join(pairs, ", ", fn {k, v} -> diag(k) <> ": " <> diag(v) end) <> "}" + + defp json(term), do: JSON.encode!(term) <> "\n" + + defp write(root, rel, bytes) do + path = Path.join(root, rel) + File.mkdir_p!(Path.dirname(path)) + File.write!(path, bytes) + end + + # `sha384sum -c SHA384SUMS`, run in the vector directory, checks every other file. Lines are + # "", sorted by path bytewise, LF. + defp write_sums(root) do + lines = + root + |> Path.join("**") + |> Path.wildcard(match_dot: true) + |> Enum.filter(&File.regular?/1) + |> Enum.map(&Path.relative_to(&1, root)) + |> Enum.reject(&(&1 == "SHA384SUMS")) + |> Enum.sort() + |> Enum.map(fn rel -> + hex = :crypto.hash(:sha384, File.read!(Path.join(root, rel))) |> Base.encode16(case: :lower) + hex <> " " <> rel <> "\n" + end) + + File.write!(Path.join(root, "SHA384SUMS"), lines) + end + + defp h(hex), do: hex |> String.replace(" ", "") |> Base.decode16!(case: :lower) +end + +N6Vectors.run(System.argv()) diff --git a/test/fixtures/n6/README.md b/test/fixtures/n6/README.md new file mode 100644 index 0000000..0fb465f --- /dev/null +++ b/test/fixtures/n6/README.md @@ -0,0 +1,98 @@ +# N6 vectors (Trinity candidate set, provisional) + +Written by `scripts/n6_vectors.exs`; do not edit by hand. The layout follows section 10 of the N6 +profile v0, draft r3, which is provisional: every rule may change until v1 is frozen with the +profile's own vectors. These are candidate input, not the published set. + +* Pin: `VERSION`, plus `SHA384SUMS`, which `sha384sum -c SHA384SUMS` checks from this directory + (lines ` `, sorted by path, LF; every file but itself). +* `*.desc.json`: the value, integers as decimal strings (`{"i":"..."}`), byte strings and text as + hex (`{"b":..}`, `{"t":..}`), arrays `{"a":[..]}`, maps `{"m":[[k,v],..]}`, `{"s":"true|false|null"}`, + and the vector's source. +* `*.expect`: `{"exit": 1, "verdict_code": ..., "source": ...}`. The draft leaves this format Open + (its VB5); this is Trinity's. +* Expected encodings are hex literals in the generator, copied from RFC 8949 Appendix A or built + by hand from CBOR heads, never output of the encoder under test. +* `body/schema.json` and `header/profile-table.json` are Trinity's test schema and test `typ` + table (the draft leaves both Open, VB6 and VB4). `thumbprint/` uses NIST ACVP's published + ML-DSA-87 keyGen case (tgId 3, tcId 51); no key of this project's is published. +* Not included: the limit vectors at 64 KiB and 1 MiB (the suite builds them), and COSE_Sign1 + samples (ML-DSA signatures are hedged, so a sample is one valid signature among many; the + suite signs and verifies where the runtime can). + +## Encoding defects + +| Directory | Expected code | +| -- | -- | +| `nonminimal_head` | `noncanonical` | +| `indefinite_length` | `indefinite_length` | +| `float_half_1.0` | `float_forbidden` | +| `float64_int_valued` | `float_forbidden` | +| `float32` | `float_forbidden` | +| `nan` | `float_forbidden` | +| `undefined` | `simple_forbidden` | +| `simple_16` | `simple_forbidden` | +| `simple_2byte` | `simple_forbidden` | +| `tag_bignum_small` | `tag_forbidden` | +| `tag_1_datetime` | `tag_forbidden` | +| `tag_cose_sign1` | `tag_forbidden` | +| `text_nfd` | `not_nfc` | +| `text_bad_utf8` | `invalid_utf8` | +| `text_overlong_utf8` | `invalid_utf8` | +| `text_surrogate` | `invalid_utf8` | +| `text_key` | `key_not_uint` | +| `nested_text_key` | `key_not_uint` | +| `negative_map_key` | `key_not_uint` | +| `map_key_over_u32` | `key_not_uint` | +| `bstr_key` | `key_not_uint` | +| `swap_keys` | `noncanonical` | +| `duplicate_key` | `duplicate_key` | +| `int_below_dcbor_range` | `int_out_of_range` | +| `trailing_bytes` | `trailing_bytes` | +| `truncated` | `truncated` | +| `too_deep` | `too_deep` | +| `not_well_formed` | `not_well_formed` | + +## Body defects (under `body/schema.json`) + +| Directory | Expected code | +| -- | -- | +| `no_schema_version` | `schema_version_missing` | +| `bool_schema_version` | `key_not_uint` | +| `negative_schema_version` | `key_not_uint` | +| `carrier_schema_key0` | `schema_reserved` | +| `unknown_schema` | `unknown_schema` | +| `unknown_key` | `unknown_key` | +| `required_key_missing` | `required_key_missing` | +| `wrong_type` | `wrong_type` | +| `set_unsorted` | `set_not_canonical` | +| `set_duplicate` | `set_not_canonical` | +| `not_a_map` | `body_not_map` | + +## Header defects (under `header/profile-table.json`) + +| Directory | Expected code | +| -- | -- | +| `alg_not_mldsa87` | `alg_not_permitted` | +| `content_type` | `content_type_forbidden` | +| `crit` | `header_label_forbidden` | +| `label_0` | `header_label_forbidden` | +| `label_5` | `header_label_forbidden` | +| `label_negative` | `header_label_forbidden` | +| `label_hash_envelope` | `header_label_forbidden` | +| `alg_missing` | `header_not_protected` | +| `kid_missing` | `header_not_protected` | +| `typ_missing` | `header_not_protected` | +| `typ_uint` | `typ_unexpected` | +| `typ_not_in_table` | `typ_unexpected` | +| `cwt_without_scitt_typ` | `header_label_forbidden` | +| `kid_wrong_size` | `kid_not_thumbprint` | +| `header_noncanonical` | `header_noncanonical` | + +## Digest defects + +| Directory | Expected code | +| -- | -- | +| `digest_alg_not_permitted` | `digest_alg_not_permitted` | +| `digest_length_mismatch` | `digest_length_mismatch` | +| `digest_not_array` | `digest_malformed` | diff --git a/test/fixtures/n6/SHA384SUMS b/test/fixtures/n6/SHA384SUMS new file mode 100644 index 0000000..d0bd763 --- /dev/null +++ b/test/fixtures/n6/SHA384SUMS @@ -0,0 +1,337 @@ +bd5172bf9a168bdd8ca067f7ce7b83c0e7f3f9badf15646beaa26c7641d7cc5778de3f2a197ce716c46d9ea4c3b9324a README.md +f3719250dcef54348054c1b7a348ad7283a28809162e3f1b4dae46460d2390b174aefda182083d5d3aaa234d78ebe481 VERSION +e80ceee3df61c92398d9f44ea69efc5ca52602afd424d63ec799735a3466f836ff7208cc45a21918b4ea314e8beb77a8 body/negative/bool_schema_version/true.cbor +c4dc77dbb660d75895e69c0e6f666e941369265a051896b15bff30b763d73cb1ce7586a45c732d1380539e54ba616ca2 body/negative/bool_schema_version/true.expect +848553192b7cfaa67a1c231f3dc290c3e63643070e616910412a6e0acf4a9ae3134ada5de86e606c8d2a0c139f25aa09 body/negative/carrier_schema_key0/envelope_schema.cbor +ab82f7cf2e7c5ef2a3e5a4a55dc7576a10178226ce45ea015c90abf19630fda2fa3e154fb17b7925a685cd2ee9d698c4 body/negative/carrier_schema_key0/envelope_schema.expect +86d7f0a9995f461034806461e96f1038289a5375e38c55477afcea8305108a2fc412861a86daf11a5cd810b4a42a8442 body/negative/carrier_schema_key0/top_of_range.cbor +ab82f7cf2e7c5ef2a3e5a4a55dc7576a10178226ce45ea015c90abf19630fda2fa3e154fb17b7925a685cd2ee9d698c4 body/negative/carrier_schema_key0/top_of_range.expect +27250788571c3c27118470ac1ba1def915373792795ca992afbf21552a707648c0f6445d86cc408a51ef2e39f9356874 body/negative/negative_schema_version/minus_1.cbor +c4dc77dbb660d75895e69c0e6f666e941369265a051896b15bff30b763d73cb1ce7586a45c732d1380539e54ba616ca2 body/negative/negative_schema_version/minus_1.expect +d289746bc7007ed6ce6010aead36ed88fe3e62da386cf4208474682a58960334c39194545b18cf6f47fb35fad086786c body/negative/no_schema_version/no_key_0.cbor +9e40ecb3c6dc2b3808ea3787fd07a31890fa41b2d336bdeb922b0b6c79543e5dedeba148a9671ba0ebb99e1f104b92a0 body/negative/no_schema_version/no_key_0.expect +9e4ad6b6d84d873013c076e0dcbe7de5f369f5cbb1c865100a5d776d1efcc2fe76c07a0e098490166b3621e057d70533 body/negative/not_a_map/array.cbor +1fd35304f4c0c1b8dc89a0454b5fffaa2c7b1333eac63c57db4d0d16050e4214a6d1083c96028a106148ec0a367268df body/negative/not_a_map/array.expect +3c0b3142d28945e9a2c5221e2da05b4b5441f8b008bafeae9472e0ab7e2fc3a2bbf989497c7fa76a039c89229880fb8d body/negative/required_key_missing/no_name.cbor +36fb156c57b913cd756338cd0f83e1a833ce3099c51e63df15be066ccfa2bd268fa75fcc04c47f86e2c52d027a818728 body/negative/required_key_missing/no_name.expect +7b5fcee12059ded8fa1afa22e204d3acb580c62aff8fe979dda2ae3bcba6316e0149bc60efec65444af40582143b4cab body/negative/set_duplicate/1_twice.cbor +c5a6ab7e084812fa66f542f8d500f87b0d0493993c8bca83fd6332d2009016989e8cb83a93c4fae1d8b08a8eb616510d body/negative/set_duplicate/1_twice.expect +b3cee3d09d5ababe25e142a385186d1a63c8b5d92ff7968845d3df5a7ba1f5e4f1ea311dfc6b269504d8e9e211642abc body/negative/set_unsorted/2_then_1.cbor +c5a6ab7e084812fa66f542f8d500f87b0d0493993c8bca83fd6332d2009016989e8cb83a93c4fae1d8b08a8eb616510d body/negative/set_unsorted/2_then_1.expect +32b7c7cf13b56f1dd265ba7ca16b082b970e996d0c63db877b87344119b2425f17f8d34a638ba8a02465b113de727ff7 body/negative/set_unsorted/longer_head_first.cbor +c5a6ab7e084812fa66f542f8d500f87b0d0493993c8bca83fd6332d2009016989e8cb83a93c4fae1d8b08a8eb616510d body/negative/set_unsorted/longer_head_first.expect +880801a67356966a784ec8dd703776ecfa4e0e393a415956da4231da1a0e60520036b6cbdc024fe6040b1e8a123ca5bf body/negative/unknown_key/key_19.cbor +5831dd0658ea99fd684c14e702579484851165f1721347c82146b5cef5baf0dd1a8815ff4e658c464967a77e6cb37e93 body/negative/unknown_key/key_19.expect +9184dbb9a6f907d6db5b91f417c74b539b7405b8d87e7d3fdd27f2b199b2aa5f99c3613b65df5c95f953debc727f8eaf body/negative/unknown_schema/version_2.cbor +5fb66206c74a2ebffd03a7de0955f691fac9c364d38f2458ac327f0ad28704f4ca91ca726bc01d8793f5e455cdf8e93d body/negative/unknown_schema/version_2.expect +bd260698c7910af7c315ba9d70987b143cd0da2988199d636f4640374a9af29bc5f5bf1a423d0dc32106d0b45e1bc9a7 body/negative/wrong_type/name_as_uint.cbor +a39dfa282986952f79d8226d3bf2f0bab2ffab497314e7fc2b765867eb123d30786b3593393989c6a34f999aa2fe6992 body/negative/wrong_type/name_as_uint.expect +aee22769da6148114745f47be7e4042a761b84b0dd2e6f949b8f65e647a49d9e1bc85a52d591b55ec30c9a85aa451ddd body/positive/all_fields.cbor +edef20d0f9adf95e7243d7f5b73741e21351e4be6f973a073724e609944a1fad8e47469d8a88090d502e166b00b63eeb body/positive/all_fields.desc.json +732fbb76961dfb6f65750004a59429857dea30894dcc9e4074e167aa127c6dd0dd92623140fdc237ede8369f3af68662 body/positive/all_fields.diag +cd3c1ed5cf88093e817f4ecfef7b65702a239ed9f17da5c0df7feb2351ecabb6a4d246f40bf886c1fa254c407c2ff3e3 body/positive/minimal.cbor +e4f3f500ee6ea5be78b8ca4b57116fb68b8bf429b0e0d9995060cedb58c2e60e6876b1271bea1291d64cc6350aa051b4 body/positive/minimal.desc.json +1cda2a11971c89d70de20926c22feb07e8698a73dbe0e66274121f60b5b134f3b975d5896ca024be98d1841fba474c17 body/positive/minimal.diag +b63c74e780fd7f3493ab66b850a33a52ec6ee6fb9a6120b66405f95f87c94532a8afb98e328046e3c0d94dc666f43efc body/positive/set_sorted_by_encoded_bytes.cbor +b8f34840b32bf53c31b2056633a3476660eca858bd14c15b1e0e1d3399c4475464e8a7f4b95ad030a0b22ad0698aab57 body/positive/set_sorted_by_encoded_bytes.desc.json +c6f0aa0ae966875501196a53c4211686c34714d8ee7d5de601c709d82d356da5503b830491477e43d29db75e62fafc35 body/positive/set_sorted_by_encoded_bytes.diag +d5e567a12927ec81b341cb6af2ac76d6ed470bd313f07107a66d5c961a89d18a24a3e43d3c05c27bed891c9dd4cfd955 body/schema.json +cb00753f45a35e8bb5a03d699ac65007272c32ab0eded1631a8b605a43ff5bed8086072ba1e7cc2358baeca134c825a7 digest/abc.in +23f24ce4bb582a0a59e71588f4cf5b2aaf21e5cc65b7ee88265940adec375c4b39187bc3fb85c33ed40c9fc87380346a digest/abc.ref.cbor +3b87f97be5c59b8c657ddfd50f91411d085e18ffc917b9e6de661c9f17f03546f6f7abdeaced4355641ed459b925f807 digest/negative/digest_alg_not_permitted/sha256.cbor +1dfe0234b117e70fa732f6cb312ee426e43ccf539216874cf0b7e8ca4c7314037763735fe5c6ab35fc51a06d1ed3fedf digest/negative/digest_alg_not_permitted/sha256.expect +cbf9e2506dd0df652dd1e0789be6f5874cf495cfa80a2aa76529656bb651f4c8462dcb5cfb38a55b2a4a8eaec3a8463c digest/negative/digest_alg_not_permitted/shake256.cbor +1dfe0234b117e70fa732f6cb312ee426e43ccf539216874cf0b7e8ca4c7314037763735fe5c6ab35fc51a06d1ed3fedf digest/negative/digest_alg_not_permitted/shake256.expect +4ed1f50a7398b4e70e7a9256f94a57a37a26cb7a536468c74c33d271294e6d36b6219f2e041e109b594342ac025576f8 digest/negative/digest_length_mismatch/sha384_32_bytes.cbor +69d71f30a77f6bc8c1adbee6bcfa3e87b34e7b9af23656c1fd56f7097775386cbd042a9d3a985840ec40155557b66320 digest/negative/digest_length_mismatch/sha384_32_bytes.expect +51f0dc1bfe44461f3d3c9aa38add80af8f504500249aecd59703d1cab03d743a6c798a1062ef6e4185041f83ac12884e digest/negative/digest_not_array/bstr.cbor +031fe6e767a613dac793cedd3092b6bbedf4cba029429b780680a13077667ac2c762080b6c16433a421bc2d5865b098f digest/negative/digest_not_array/bstr.expect +8180df2c9ca43ea9cfee65367c45a73365a5f4b1f3fd4f4af610cad9e9f9c00e6d6d25a5cb245ed710d0ed2a2b5446bc encoding/negative/bstr_key/h01.cbor +c4dc77dbb660d75895e69c0e6f666e941369265a051896b15bff30b763d73cb1ce7586a45c732d1380539e54ba616ca2 encoding/negative/bstr_key/h01.expect +877a1d9f501e49ed84faebba15c4db73e2f416e17444cdc1bae655fddd840bcf47fc2d4909375641d36e8d7f7b19b0a2 encoding/negative/duplicate_key/1_twice.cbor +973c3d180cc81610206838c07b7d6176f110c837efb2ac82848a06beff946e40659ddf5ae8354f93f41b7a114bd16c93 encoding/negative/duplicate_key/1_twice.expect +eed4108d2d07e18541873b8271d895334369edc2a25cf3426c0faaf0b6f90d79dbb31e4541d3d93984c9309575f08dcd encoding/negative/duplicate_key/nested.cbor +973c3d180cc81610206838c07b7d6176f110c837efb2ac82848a06beff946e40659ddf5ae8354f93f41b7a114bd16c93 encoding/negative/duplicate_key/nested.expect +c1423a615b288da12b36ada16a9b90f09d34ee9e1347bbc4ba4dd57b5fd47bfea922a26ef4b85a1ae28e8ea2752463db encoding/negative/float32/100000.cbor +ae368675ca0d7f601f5bc5455a59b49f772cb73aff45ff2630093f5066531f8b47a5c7980702d4d6ecd3701206f9b546 encoding/negative/float32/100000.expect +a3d2a1dbcbbd122baa367b43cd3a2c182cbb19e5be8ec01e624fac2fe253acea4c2ce704f5e692f5a5ff51051f21dc63 encoding/negative/float64_int_valued/1e300.cbor +ae368675ca0d7f601f5bc5455a59b49f772cb73aff45ff2630093f5066531f8b47a5c7980702d4d6ecd3701206f9b546 encoding/negative/float64_int_valued/1e300.expect +6c28d10eb095e2e0e8cf0013c33e460eb1ed78cff3b66f1b5746010607f141c68c96c751a3ec1a0e98b53eb8c457666a encoding/negative/float64_int_valued/nested_in_map_value.cbor +701d4bda21a866c55f3c355651fc9f4a85ecd91b95a3f0c7e4d068d70ba4351b57522d5d5a6b99bfcc378dd3690a2fa7 encoding/negative/float64_int_valued/nested_in_map_value.expect +e16d77ef3b11e188173a90ffffffa086a8806a8844b4f6308e9a5ab116ed9bc52e241261721cf7307d417eda808c9947 encoding/negative/float_half_1.0/negative_zero.cbor +ae368675ca0d7f601f5bc5455a59b49f772cb73aff45ff2630093f5066531f8b47a5c7980702d4d6ecd3701206f9b546 encoding/negative/float_half_1.0/negative_zero.expect +acc331798cd3722790a5a0cf2846641667921b3b341ac4303681e01eafa0a1c8d0f1b0e3887b4551931e3c90e22faa8e encoding/negative/float_half_1.0/top.cbor +ae368675ca0d7f601f5bc5455a59b49f772cb73aff45ff2630093f5066531f8b47a5c7980702d4d6ecd3701206f9b546 encoding/negative/float_half_1.0/top.expect +1992fc6549fc5ba6df491e956fdb4bc3efe468efa4e73ab2335e9b461733309e55eb2ccaaba6cfe2b2c4f1d3a30bd480 encoding/negative/float_half_1.0/zero.cbor +ae368675ca0d7f601f5bc5455a59b49f772cb73aff45ff2630093f5066531f8b47a5c7980702d4d6ecd3701206f9b546 encoding/negative/float_half_1.0/zero.expect +bdae04ae7d21686c6abdbfb24eb0fd752a0263e80f7257ed96109778daffa62796ea605cf6a84deb16a714f8bf40ac37 encoding/negative/indefinite_length/array_empty.cbor +c5195520312fe5187c1b51d05d4219eb160cfbe1825e002c93bed8b4b0b6b15c4769ab53a03d1b63eb4a0c131574a806 encoding/negative/indefinite_length/array_empty.expect +2e885a6d0874108ffc8ff09adfc157d1ac11356b6f64f1f82f35c9ec2bc7f93e4268d146665ce8f5d199090c93f79146 encoding/negative/indefinite_length/bstr.cbor +c5195520312fe5187c1b51d05d4219eb160cfbe1825e002c93bed8b4b0b6b15c4769ab53a03d1b63eb4a0c131574a806 encoding/negative/indefinite_length/bstr.expect +2b301ccc6ee384615706062e3f2a867fcc100ff1e04b2c4870645d17420c6b20b95384e2a2bc3cf52a97c18c536e01f8 encoding/negative/indefinite_length/map.cbor +c36e761472a1fb8c4ddb56395f79981d8871bda4d26ceb365085d83f7c53bebf5cafb28fee58b147e0c0cf45daac6ccf encoding/negative/indefinite_length/map.expect +313c8ebebe8be850f401eb44e079f912e22e9f787494ee6d5baf68567fa1d6b2868a6fd0f4b680879e5b902182def9c3 encoding/negative/indefinite_length/nested_in_array.cbor +c5195520312fe5187c1b51d05d4219eb160cfbe1825e002c93bed8b4b0b6b15c4769ab53a03d1b63eb4a0c131574a806 encoding/negative/indefinite_length/nested_in_array.expect +dd31ef5e625a204ed7299649702eb5ae947b6685368d406c3feb4f1d1361f6252b599161ceace7284cf81859b6da8eb9 encoding/negative/indefinite_length/tstr.cbor +c5195520312fe5187c1b51d05d4219eb160cfbe1825e002c93bed8b4b0b6b15c4769ab53a03d1b63eb4a0c131574a806 encoding/negative/indefinite_length/tstr.expect +ff3719a820e72bd1cf40f75876cf79ce8be828017788b8535501c372ce9af4dc6c1b546b2bf448dc62818bdc66e946d3 encoding/negative/int_below_dcbor_range/minus_2_63_minus_1.cbor +8ea96c18a3e9cb996bcbfc33293824d84963029b89ad7409376172d22eb4b12eb154813b7edd14a18071a74a8b9c0adf encoding/negative/int_below_dcbor_range/minus_2_63_minus_1.expect +817876637a42f4ef718a946b2abfc566a94baf9fb7cd039ee2d21ee24078f1f290097550828bfc3af6d1fc60b11c062b encoding/negative/int_below_dcbor_range/rfc_minus_2_64.cbor +d1eb7d783f2c37545f403e5a71bf83a6cfb05995f4c454eaf53f82d4dcf5688be6c82d16e969328255c76258b179b34f encoding/negative/int_below_dcbor_range/rfc_minus_2_64.expect +704a87dbb5abea22ff8d45b0e4ccf6900d70d74ed54fcf40d96a64956f6b94081104b77b96e00c69916f27c4dec11669 encoding/negative/map_key_over_u32/two_to_32.cbor +c4dc77dbb660d75895e69c0e6f666e941369265a051896b15bff30b763d73cb1ce7586a45c732d1380539e54ba616ca2 encoding/negative/map_key_over_u32/two_to_32.expect +523f008df31f6b7fbd4cb1f8837fa4eab19af979fafc5e33d05e604e28d76806453ee767f8b54d70bfbaf84a4ef490eb encoding/negative/nan/double.cbor +ae368675ca0d7f601f5bc5455a59b49f772cb73aff45ff2630093f5066531f8b47a5c7980702d4d6ecd3701206f9b546 encoding/negative/nan/double.expect +6ccdecbd8b9606ac3cefe2dcf66ceec1920d63d8580f7c4478d5bd3efb996c3767c0227432a6e8b524316bf020f67701 encoding/negative/nan/half.cbor +ae368675ca0d7f601f5bc5455a59b49f772cb73aff45ff2630093f5066531f8b47a5c7980702d4d6ecd3701206f9b546 encoding/negative/nan/half.expect +88f3c1e9213fc1848da5055b4a77184a108ab8a8115fb35f4d274c13ddb66aa392fbfc98e6d4f443b918cbb7f3990e40 encoding/negative/nan/single.cbor +ae368675ca0d7f601f5bc5455a59b49f772cb73aff45ff2630093f5066531f8b47a5c7980702d4d6ecd3701206f9b546 encoding/negative/nan/single.expect +2eb775bd106dc616bbe4d398f86e62bbb2daf6b28228d7f68dee55d63b4ad20d3dcdf445f021067a6f00ec87f42f86f5 encoding/negative/negative_map_key/minus_1.cbor +c4dc77dbb660d75895e69c0e6f666e941369265a051896b15bff30b763d73cb1ce7586a45c732d1380539e54ba616ca2 encoding/negative/negative_map_key/minus_1.expect +3ba8ca4b3c0b5f1a1d8c6b95487b9c8b759de9ef9218eac58f6d23f5434f802e8337ebc1960f82f551c0259e3d27a241 encoding/negative/nested_text_key/in_array.cbor +7acfa4c634f68293692fb7e83e4bf7f1f8e93656c71405c6a3c8e779dbf64a3f58037e6cfad7eeca8afa00583b83f647 encoding/negative/nested_text_key/in_array.expect +7b2a36bb16927bc628db4fa727da3ed36fd666aa9e80b8d79ebb50e3ceb7737dc5ad2ecb50478d4d7265899df4c1aff8 encoding/negative/nonminimal_head/length_in_two_bytes.cbor +b3d1981309035b76016f6266b6a5db1904f976c6ce40a1ad6e861a061a98dc10c414b8392528d70ee75a1e71b059280b encoding/negative/nonminimal_head/length_in_two_bytes.expect +d461f3bd601eb0f06802d0ce51add3577bfd521a5920596554efd69e0c81174ca1bd48683b79cdbcbadad218a77cc4e3 encoding/negative/nonminimal_head/nested_in_array.cbor +b3d1981309035b76016f6266b6a5db1904f976c6ce40a1ad6e861a061a98dc10c414b8392528d70ee75a1e71b059280b encoding/negative/nonminimal_head/nested_in_array.expect +5b0c43489c2d90a1af326b7c12c8da5041ac66ff81d84ec2b33ff4b925361856591dbea3a7295afe0e68e05b06ae6323 encoding/negative/nonminimal_head/uint_0_in_one_byte.cbor +b3d1981309035b76016f6266b6a5db1904f976c6ce40a1ad6e861a061a98dc10c414b8392528d70ee75a1e71b059280b encoding/negative/nonminimal_head/uint_0_in_one_byte.expect +6cc2d84843a5bdd7c374ce72d5f5f5879baed501881c164f810986b5aaecfcff66760f32f23caed55fc1fe50a6de70f7 encoding/negative/nonminimal_head/uint_23_in_one_byte.cbor +b3d1981309035b76016f6266b6a5db1904f976c6ce40a1ad6e861a061a98dc10c414b8392528d70ee75a1e71b059280b encoding/negative/nonminimal_head/uint_23_in_one_byte.expect +3631105e0c81e6e3f86d84013747270dd9fbd3e07e68ce002470a8bd66c4538abd7a0b638e1a1fd5b80e5f579c039d7d encoding/negative/nonminimal_head/uint_255_in_two_bytes.cbor +b3d1981309035b76016f6266b6a5db1904f976c6ce40a1ad6e861a061a98dc10c414b8392528d70ee75a1e71b059280b encoding/negative/nonminimal_head/uint_255_in_two_bytes.expect +c957bc2f68b7df675b64cbd1d26f62c5d924b3679bb06b2994cc4b43e0b4ab6fdae5d239679220fb04e5243ce78699b2 encoding/negative/not_well_formed/ai_28.cbor +0d5a8b732b2967b4ee75293730ffe2914dba10d9f7527c87f05c395e95dc2b695b7cb6195b8b8d3480793be4b4c41633 encoding/negative/not_well_formed/ai_28.expect +3c86581da28edd0ecfce7d2114a7a30dfd113520f43fb1f26cdec393b5a812993be397b3f791d818ed7543908ce1780e encoding/negative/not_well_formed/ai_30_nint.cbor +4df1e781d8769579a8ac760edd154275437b987ce93090edfa59c277261aec00e6ef426521405b83a55933fb4bc125cb encoding/negative/not_well_formed/ai_30_nint.expect +f78061d1a08db11ea17ca7ec7a25f19d71b4887dc0d7e0305d194a81d0e46830565167047616cf03d94bcde678c73172 encoding/negative/not_well_formed/f8_below_32.cbor +5c1dfe0edd3081f06bc5aafb9b8de6802aa45e46ad12d94da83cfaae90625b92d669833ad83a13f8ccc034a5314f8179 encoding/negative/not_well_formed/f8_below_32.expect +43950796d9883503655e35b5190aee687a2dd99f265012625b95753978e4efff3e8414d178a6e2318480d8eb6ddee643 encoding/negative/not_well_formed/lone_break.cbor +4df1e781d8769579a8ac760edd154275437b987ce93090edfa59c277261aec00e6ef426521405b83a55933fb4bc125cb encoding/negative/not_well_formed/lone_break.expect +6ee429a73772e150ecf0338e46d8dfce8e314cf7854514509059796b1cbb809c666dd640c986e0ddb3b95ca9fc77f109 encoding/negative/not_well_formed/uint_ai_31.cbor +4df1e781d8769579a8ac760edd154275437b987ce93090edfa59c277261aec00e6ef426521405b83a55933fb4bc125cb encoding/negative/not_well_formed/uint_ai_31.expect +afee6610f1105c97f7c1b7cdde80d7d9b82028fb20d0f2724a3d0cb587d978f545798afaa751b4f344c57b349bf54538 encoding/negative/simple_16/top.cbor +2f0b64f103235e19787607f05056bc1c68d1c46a6d1e871a2413beb245be7811e13e9a5626edef2e8065d6ec5833bdba encoding/negative/simple_16/top.expect +c0610c31217f7680374e6c55af1f91988454cccd840d26634a41bfc781ae7f9b9e98a4fb2eef33ad1144e3d736dc2d38 encoding/negative/simple_2byte/simple_255.cbor +2f0b64f103235e19787607f05056bc1c68d1c46a6d1e871a2413beb245be7811e13e9a5626edef2e8065d6ec5833bdba encoding/negative/simple_2byte/simple_255.expect +67edea99286da00f058506038fe1a6df1b07f2dc8b9b4139b9e12a98c7fdd53c1ce2dcf24d61a41f39227db48f26f81e encoding/negative/simple_2byte/simple_32.cbor +3ef4fa5394abc275aef44df5ab35b3623d5892c29307bab078b93a0cee17c178f21bb512a46945d6af9ef56f1485a2e0 encoding/negative/simple_2byte/simple_32.expect +57278da857680ca5f525fe0816b2e8d410bb82dbfe1eaf20e21299a1a9c3e2c76e4ea0b472f17e0fbc504ab0d49564af encoding/negative/swap_keys/24_then_23.cbor +b93bd929de03cb02f7ce274bf3273603e6aa240bb4e7446de8de344def5558ebc4abe90b8cd9d0ea5e81ece69ce24150 encoding/negative/swap_keys/24_then_23.expect +bc8e9a77f65b89eb1addddf2bef71e94149381d92af39df30cf65afc4f19d3aa9b203edfd7e47e2fb84310600fcf0626 encoding/negative/swap_keys/2_then_1.cbor +b3d1981309035b76016f6266b6a5db1904f976c6ce40a1ad6e861a061a98dc10c414b8392528d70ee75a1e71b059280b encoding/negative/swap_keys/2_then_1.expect +582b6a17211b41eb6e46d87489e677a15455c2e3808ea6fb8c713821be99b81a6c1ca0c597968b4fc32f33da838580ee encoding/negative/tag_1_datetime/epoch.cbor +b4df40a6944944d401cf11072e0a49e5bb12f4c86635826c3ee84c8869732d29c507ca080acec5323206cb7d71d93f5a encoding/negative/tag_1_datetime/epoch.expect +8f7ac4c3284a5a9a203f5116f52c0caf25e54f7ee6c6f05611225279437d0745e73a35942af2fba1f642a71b770a0585 encoding/negative/tag_1_datetime/nested_in_array.cbor +f6473c3a453c7392810e4e5e9b41cd1bcd3968ba7799387ac0dcc6bc59a7190c4172cb05b4a0b26998d7a61f0cc312f4 encoding/negative/tag_1_datetime/nested_in_array.expect +4128a24e64564812d210bf675f73cd7cf6fa1a8dd46497c1b3955b9a21f9be546834ea4270cb5392bc43e3b38359a185 encoding/negative/tag_bignum_small/positive_bignum_1.cbor +f6473c3a453c7392810e4e5e9b41cd1bcd3968ba7799387ac0dcc6bc59a7190c4172cb05b4a0b26998d7a61f0cc312f4 encoding/negative/tag_bignum_small/positive_bignum_1.expect +97cda515521f9bde942b97883eb88867bd6fa1812b7954306ab164febfbde238bae334542dace87f2af483b55ac0eeb6 encoding/negative/tag_bignum_small/rfc_2_64.cbor +b4df40a6944944d401cf11072e0a49e5bb12f4c86635826c3ee84c8869732d29c507ca080acec5323206cb7d71d93f5a encoding/negative/tag_bignum_small/rfc_2_64.expect +89e16ad03cb353fd895c668935218478c9b6a631b92029611fb1f60b504e4d33650124d67c60e9af70af8b8fae160b72 encoding/negative/tag_cose_sign1/tag_18.cbor +49b6ebffd77391c5d4b16a702607aa1514616acbb1ab2a691d6f0a988e2f4b809af4177e0554d6bc4778f9f64023fda4 encoding/negative/tag_cose_sign1/tag_18.expect +e4216aa79474978ac9e2acf8353589750fb8f08c169d91c4cbbbfcdc4a7d31ca507188dca2b7586e0b00569d08514432 encoding/negative/text_bad_utf8/lone_ff.cbor +c70429b922859cbb9a0e4686100da09ffca08f24167e0543f0bacd579d3937f76d522c2d24f5b8c6bd9804663b92d831 encoding/negative/text_bad_utf8/lone_ff.expect +4b4783772e3d538f56a7c0389ffd4509fb8f3223f8d738bdbd22780c23a17e7f5bd7c898879fb3b6a5b5801230fb05c7 encoding/negative/text_bad_utf8/truncated_sequence.cbor +c70429b922859cbb9a0e4686100da09ffca08f24167e0543f0bacd579d3937f76d522c2d24f5b8c6bd9804663b92d831 encoding/negative/text_bad_utf8/truncated_sequence.expect +c4d31c9d4cb2589592da0ffa786e05164208c29412066593ebdc430a29b5ce131d977ffb0e559d055ad7d14e86f4cd33 encoding/negative/text_key/rfc_map.cbor +7acfa4c634f68293692fb7e83e4bf7f1f8e93656c71405c6a3c8e779dbf64a3f58037e6cfad7eeca8afa00583b83f647 encoding/negative/text_key/rfc_map.expect +5c7397cafd3d2fb3dc3a6799ea312f5da48a7a4e29032fe2e0f335affafd5df38813ee30419515a2e020ba5566117abd encoding/negative/text_key/top.cbor +c4dc77dbb660d75895e69c0e6f666e941369265a051896b15bff30b763d73cb1ce7586a45c732d1380539e54ba616ca2 encoding/negative/text_key/top.expect +99e6b8bde6e3b5f69601a2b0221f070c863eac551712d53789e3481acb04d51b334935fa909cc12cec45a10a63b50edb encoding/negative/text_nfd/e_combining_acute.cbor +aed855ba540e02c0f0a9eb84e6213d57adf12994395f2bb6e7f80140d64c1b30d4791a7150168a7a95759462349f35ff encoding/negative/text_nfd/e_combining_acute.expect +4d911fc0d387268a5045d312a9fbed80bf9bb96593d3316b7c08d3befadbf3ef78646f7cb4bbdd3cd956d2918f402bed encoding/negative/text_nfd/nested_in_map_value.cbor +0310b08c808ce5bc9a53945e47a358542c55b4e06cd47443a246ecaad562db15a24ec8b77f93a12c36683078200b8c76 encoding/negative/text_nfd/nested_in_map_value.expect +dcb09970c39ef25dce96726fbd0f74a3f24c8efb0952a8107b40be5c88cc6ddfe1fc5ce0a1dccd9e9d92f18b34880727 encoding/negative/text_overlong_utf8/slash.cbor +b18f706318882bc7e16b912f629c8e81339ae33b3e7e929a9165bdaed4af0578df58f6773526c3ddc6a13f2d620f96c4 encoding/negative/text_overlong_utf8/slash.expect +0b3ceebc7e4e5f26eb52c04b3200156c121c5f4425695b2deec2935f4808f5a5f1376fbba2d890533216be589be85cde encoding/negative/text_surrogate/d800.cbor +a5a1ec6004a65d0decadb1a603c6f740e69f2f683a2e62364e71f5ac5e4521c50512ac91b89cb7a4ad67c85f9299ac37 encoding/negative/text_surrogate/d800.expect +9713906ccad389e779cb0b5e6c5b20511ccbfc2ce4539d211ea9aa1a1d706e072349747e9866ce20657488093d51e975 encoding/negative/too_deep/depth_17.cbor +41a52faa7a8ff846746e9d83e1baf6e37a5b2c073853f83fae5a5730d2d39c4a836974949b0d9c5a7d2e73e0323aae97 encoding/negative/too_deep/depth_17.expect +29235c2fee6c8ef1a7cc772d557258f17a10a7ba3051dd1406f7c8391ac32614d305151b551ca53a3779ed7fd3d9f672 encoding/negative/trailing_bytes/after_array.cbor +6582e5721555f4da04c16bb9de4b6a244f6ae14614a375479fc28c8e8a152e5f403dfe65da9f8f149f3777e2a5659ffd encoding/negative/trailing_bytes/after_array.expect +1dd6f7b457ad880d840d41c961283bab688e94e4b59359ea45686581e90feccea3c624b1226113f824f315eb60ae0a7c encoding/negative/trailing_bytes/two_items.cbor +6582e5721555f4da04c16bb9de4b6a244f6ae14614a375479fc28c8e8a152e5f403dfe65da9f8f149f3777e2a5659ffd encoding/negative/trailing_bytes/two_items.expect +e1d6581903d81b4ca0d948bfee7212af764278b977ce8decd969773f2df356bb46cddfb0ebbf8a4bde9ca97216e766bf encoding/negative/truncated/array_items.cbor +f6bbe235f8aef1d7b0ce6e0d83b2e99c37a4bcd0fd108b1fa75f67c2317511b7b392ae69a9535029e1b733d7d1adf696 encoding/negative/truncated/array_items.expect +707f5b8704f47a24ef17557911a888b95257bf9308b1a62020852dda0bf5ffa9f7c3dbf404ad90998b4e20c7946271bf encoding/negative/truncated/bstr_body.cbor +f6bbe235f8aef1d7b0ce6e0d83b2e99c37a4bcd0fd108b1fa75f67c2317511b7b392ae69a9535029e1b733d7d1adf696 encoding/negative/truncated/bstr_body.expect +38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b encoding/negative/truncated/empty_input.cbor +f6bbe235f8aef1d7b0ce6e0d83b2e99c37a4bcd0fd108b1fa75f67c2317511b7b392ae69a9535029e1b733d7d1adf696 encoding/negative/truncated/empty_input.expect +2d4f5c572cdf50bdaa1d615d787131450d4fabf5f6514e8197087ae3bdddb4be2a987a7bea309cffa497ef1f010d6fd4 encoding/negative/truncated/head_argument.cbor +f6bbe235f8aef1d7b0ce6e0d83b2e99c37a4bcd0fd108b1fa75f67c2317511b7b392ae69a9535029e1b733d7d1adf696 encoding/negative/truncated/head_argument.expect +46da6c0351bd8224fe62b9d82156314d1a05bcc8bf1e755b050d7cf633d1c1e34c16b763ed85ceb6629dbe91501e2ce0 encoding/negative/undefined/top.cbor +2f0b64f103235e19787607f05056bc1c68d1c46a6d1e871a2413beb245be7811e13e9a5626edef2e8065d6ec5833bdba encoding/negative/undefined/top.expect +a95fea7eafd96fab8701c6c221d31135ea8681b8a2d3889446de90431cc436c5f0a495d1cc000bc8787018391a5c6979 encoding/positive/array_123.cbor +cadf5dacc794484f3675441bc49c57e7d5351ec0d5c13542e909b10bbf65c848302ef5f3ee48809f759d73be70679aee encoding/positive/array_123.desc.json +6c70775e3c51cf4eb1069f7206524584173cf8f0c148040a246ef3ef55d90a9ceaa19db9ff22084a84c0455674a0452c encoding/positive/array_123.diag +8fdd2f7315eb73fe84f56765d47b88b70aeada787c789c20cac0e2e7c1c4d18049e6b003c9d9e64a17551a33bd2f2b11 encoding/positive/array_25.cbor +fa173778223aedf56811b9d48475a620a4089206fc794143f96efa7f23a69b671784e23974293b5b472b4ecc376a70af encoding/positive/array_25.desc.json +12fde41c7ca49c43e1fbacdbae64cf73b1354f48e21be462b99ec3ea53d2c4ac0a323b1cabf73a0da8f3c6e1bed6fc4a encoding/positive/array_25.diag +8db630b3036f40e29aa4c95e3c0156f55a593bf005fc425936d2134a4fc0b855bb4ab4767dd4b0b1dd261cd111598d3d encoding/positive/array_empty.cbor +d68f6cacf8c2b5fb990d4ba5839b27711327d95f75d2a4b7a2b105272a7a27be143cadf0886835acaa1943993ba358a7 encoding/positive/array_empty.desc.json +811c1b842c19f3607991bdf3b0ef507f35b9796211ade7bb8a55bb4b48ea5b02a4ef35d5abf1c7e80e40c651cf534b06 encoding/positive/array_empty.diag +11026b6ab1f007384bef08ea20c95655fe698c9e898f9302e86f33c0218dd43bca847c0cf5cb08d3f3db6623b95c9db2 encoding/positive/array_nested.cbor +dced8d4e29fb66e7100a815e6dd184fb1c0eddaf581c6efca257a4825e94233e330b3068c1b2930eef49067913671331 encoding/positive/array_nested.desc.json +ff8f16d01eae15039f47c7e3d13885515ab4b462ebf07a52144055454d8ecfa8b9656782a4ac756f3d168049209975b9 encoding/positive/array_nested.diag +fc5fe4a8b34d1523d5ddc71c7659d38a13dabff38b04451c9fba63ff08e25669fafa85ddec4d943442d711d7dbeb9e58 encoding/positive/bstr_01020304.cbor +af716f7a5f0a8a3fbf06e4b896ec4ac5dcc8c780f79e7f58889c2847817a0adbe0174f5377df4b8265fb9d6fc41bc7f5 encoding/positive/bstr_01020304.desc.json +010491556d61857f564a18579b058110e165b57e06888b6e90deb9ff1d3dd4f322bea7edb303cb080c1c02f46dbea64d encoding/positive/bstr_01020304.diag +0d7bd85ff682db03ea1a3d64ace8a89d5cc253a350ed7ab0de6b3db08d20e8d675c21e9ee402a3e224bfee2f4303cb0d encoding/positive/bstr_empty.cbor +821285a2cef3bb01a0ba20aa1894a874eb4aa7e0274a6acd933edcc3f8447616a4f98b25c81c52231a5512aa3579e41e encoding/positive/bstr_empty.desc.json +c884115ea1ac07542a6dc5658cadc957a84255fd071fe45c24ebd6e2f8800688bbbee5f95be1a0426c561de0cd3d6521 encoding/positive/bstr_empty.diag +259a6b91ddabd100553e67ed010a7a472d53584f0c0d858d2fe81861307fab198184c554719408c73b9190d1c7342f3d encoding/positive/depth_16.cbor +ec24165f90dd8d365b91397357465ce49eaf2b6eae5d0bc836a206401115896c179b7858eaf9deb86958fe7f147c7eca encoding/positive/depth_16.desc.json +c833f2890eee3e204d938be739246f4fa5352188acdf2f0c224713cbd92c7c106907ae9084b9b778bfce54d558a29e0f encoding/positive/depth_16.diag +98e2f9f75893f2238afd48c7a087ba9ec70ea6a6308853066b970fbdd8c665619180bec81ec8484b22bac423e85e8e60 encoding/positive/false.cbor +e80407851e56cc370d310aeda39498e587f848b9e78c2918961cc80aaddb7c29491933b31d49883fb7b029a09b95a2bf encoding/positive/false.desc.json +1817118af5b0f6dccc6111de38c7db1a83b7e7ec5d99efa3b817c46dfc5879224ffc33ac085e36ad8c9b80b2f1282686 encoding/positive/false.diag +27d51883651b4f9305a56a4f9b173b510a9eed73de082fd9c562263d87005fe03f410d7fccc4aa5e9752ed749c1fae97 encoding/positive/map_1_2_3_4.cbor +fec3ee1aae73c46cdc881fe62f12a97dbc81db79312c6aced1b60378047dcc7dc97e761c0f78087ddad0d8e37a4486a3 encoding/positive/map_1_2_3_4.desc.json +6da46e1c2ae6b1c3b1aab64c343e05f456e62e0bf736ec40a78f1a707907a59970ce54383fe64aea044542085251bba8 encoding/positive/map_1_2_3_4.diag +79cbe0a2e6db246b4f2a60e464eae842cf4e3c8dba2928c6edda2c205ca979d8ae3cb9fa1cc52c29dc727b841f74334c encoding/positive/map_empty.cbor +cff3ab9347401306a0babe1ea1d3b2f5f14b97e5f4c1b23bbcca6d097886593b3aeebbe04bfc1113c2133a388add93d8 encoding/positive/map_empty.desc.json +69ada93a3c8690e59d503c7bf06ad10bc064fe4dbefea84745738659f9b561aab0520a68389088af6c82b8b455a049bb encoding/positive/map_empty.diag +9a1133024df41a8a17a1f82d2b1e46f3e557d914192c685529029cb0fa7829d1f8f21857b0d8053f143800e71068cf67 encoding/positive/map_key_max_u32.cbor +e5bf32c9da839536ccbd8420f4b674d1192dbe553a80a103ab1140d29aee43d496a8ccd10838edef1061a305de1e2c7c encoding/positive/map_key_max_u32.desc.json +dc201e452509adb98372803a92ba9b92b9757f04d9a89e2b5166988a355ad85e17ffba0a1c7e80eb7d25754637105443 encoding/positive/map_key_max_u32.diag +0a5507dd206b9967dfce01d70230b51e643f9bd3be57fcfdbc9c96bee1d3fcb3defb6f41862c74db9f84c68570b7aa26 encoding/positive/map_keys_across_heads.cbor +eb14fdae7277f2a2a437bf9d122f05035b41ef9d94b3e27152f01981ba4b336a7c3ef8c89ccb0944e259abc00a0c60ec encoding/positive/map_keys_across_heads.desc.json +21a17c3cf02c910728472a1d1e65c7f8b2db157ea1b1adfe5b15ac61eb836809a38c288340927999edaeb2490eaffed7 encoding/positive/map_keys_across_heads.diag +3f972f7046c5bfb4e76939b6561525e5db7c024e6c14175496e67ba5679d6c62d6a7163694c71076968102efb1718f4e encoding/positive/map_mixed_values.cbor +04496b11228e6fe9aa6d83ae7838af4fc204cfedc6cf1f27779437df59961b184319e49e372d827393c0f5c343d931d2 encoding/positive/map_mixed_values.desc.json +a64b8052f46a553ce94501f6fcd334ff2e0401e0a2efeb6961f33f12f7265e38ecf96c3f0923fd44b100806eb3ca3ac8 encoding/positive/map_mixed_values.diag +588016eb10045dd85834d67d187d6b97858f38c58c690320c4a64e0c2f92eebd9f1bd74de256e8268815905159449566 encoding/positive/nint_1.cbor +cb56fc9906ec451c649e9cb431afbef8683d5fda05117fbded03f2629c72c2514385c8711c45c909865fb0c646a98d3a encoding/positive/nint_1.desc.json +dedd3d3317eb2848219e07dfef251430b23b51b41918a44998688e2fcc239fab47bd84002e62bfc20bdfda88ed2a1e6b encoding/positive/nint_1.diag +d54422c890a01f0f470177182909a49cd99ca8e3015e5a23d18b0471e019cd3f71ccbbb77736b83f228e4e928b59d8e3 encoding/positive/nint_10.cbor +5b434f0271e2879dd7bbe9b1e540a01eb7884eb96c77b86dda70dd1c86fcedefa03c1a751aec3a2978858d4ef9c6a227 encoding/positive/nint_10.desc.json +0826405b1bcdb32504abd08714d30f3f23ffbe34653a02e708a8076661c53ea27ccd3a169f7d73f8e92bb3973248c32b encoding/positive/nint_10.diag +34d69a8fd2da685be602c82ca99ebaa6430e52b70a2530066064d3feb6c2c5e236268b3a30d3d68661a7404275196c3d encoding/positive/nint_100.cbor +5e723d7ce987946600538fe2f2fd64948b93c4b08bca8b52867f545a87909923ee4619ca07e1748094945fdbf68c90a5 encoding/positive/nint_100.desc.json +9f253211c30f3d3c578c808476f3cea0f6f007fc2c16de12a64988ce755f4f10d3a258142b98db079604c8c02600ebfc encoding/positive/nint_100.diag +0a6ebbebe88449bccdd8144353d517ae601f8b7e6c0bf4b041d16f68566574c46e4374edca2f8477d5a1b1669e03c2df encoding/positive/nint_1000.cbor +fc84ca064f2ed1f157257bc63cc5bb6faf7822b2c13af6577a6f50bf3c588b05b2ae8576d240f800f89de3805ff20130 encoding/positive/nint_1000.desc.json +6c96ca7a5add845c04f1575a392e4c7bd904edc6dc68eac70a657d35940906d3cbe43e6418a6890e7dd75ffbc413f898 encoding/positive/nint_1000.diag +0fb8eee2ad710770396ca4494d508a09284341d18952b526062b56a5c940e4c35005154030a273533ce903ee575e46aa encoding/positive/nint_min_i64.cbor +faab4e878a3b482c906619a4b703e7872acf5e75619a973fe1c43ac0c8726fc6618a6017e9ac3ddf342f5a653b8226ca encoding/positive/nint_min_i64.desc.json +a1480f376b2a15f6b80ef1453f712838b3c28a3fd050669db36f39539b1f28e554c9642fb09567e9080bd8dd1935641f encoding/positive/nint_min_i64.diag +30ca31901978f4e90ec93f0dbfd04c087e8ff367ae0ca991df9631290133f9ca633efbf5fbb667bdc9c3888165a7a6b4 encoding/positive/null.cbor +29353bf40fdfec499eb19f0d1df433b8cbb7f794132c361aa4f2533885e4e9a90b881096fa13151e7f3f1a0812b9c626 encoding/positive/null.desc.json +57ab1308bc902bebe20145d77170b72d80af8eec8eb75bcb071f6afad6a8190c827ee1c9f622435e7271dc0e51f982a8 encoding/positive/null.diag +d4446f8e23ca22e0a59747452d32a1bee70081405c25b16be1208804233712048227001f37b675e55f76c3680a27ec96 encoding/positive/true.cbor +30a47a41664c604c65a3c92c42e39915a6df6832ba4e91b79806aed63c14a4228e3d70647587d32324d1781fd30b575e encoding/positive/true.desc.json +15f7439808b3c072cfa7c931c8e3c4c0d722afd8035b344fc4b237f9645592c0a2b4619cba5d3f1d1a61200e59ec66be encoding/positive/true.diag +c1a447ce5671c0dfd7f920ce5e977cc9afd6323854df6573c96bfee8f1871fb35564c3c42d4a968d20f59a19ebd11eea encoding/positive/tstr_a.cbor +94be53a1c271902c78183fb07e4da368f082b621f3bf36e0698b2e225cb7a55385be690a77e523b63b0816fa2191c63f encoding/positive/tstr_a.desc.json +c192e8ed095cc75090315e53e17d73ba9b2fdc96fae886d7fdd3039fc3d54644b41c39c5a24b4884b35be444eaeb3db2 encoding/positive/tstr_a.diag +1bcb3bac9e57826cd19253c00c1cf25349a030cb33128a2bb6edaa0d6001c1dee1f691970f9f90fc0b84a2a4ce82fd97 encoding/positive/tstr_aegean_10151.cbor +fbaa213dbe6c3b148c91cd7069b9955385e2ea7340753e9b9dd81bbdb3a18b5c881dae09f21d6b8690c65aacfb102bcb encoding/positive/tstr_aegean_10151.desc.json +034c8afa510b425ce4089fe33022cd385db69c557cdef92f49d87205cb51cc4a92c772d94be84ab9e7c17c3092cb1f67 encoding/positive/tstr_aegean_10151.diag +a07a87655475bce6d39650fe74b4ee3a58c05ba5defbc55db3bd0239661d6834f74381817a460a7ff497d36e98edd3aa encoding/positive/tstr_empty.cbor +dafd8435100acff30927ed2af52d2acc3d0f9ecea846bb5aff9e6f03de831c79796bfbbfc3d68cac91f72049c1a5ad4c encoding/positive/tstr_empty.desc.json +49e65592481360df764d9acac0c0eff85faac92336a1d6f6e0f82ecd695672a29a093e36dec5d72fd8107e0c4240d7c5 encoding/positive/tstr_empty.diag +5076a1d7dc014de66f3d0ebd0d56fe09c19b1a7532def6ea2af7939654325b678719f006800f9f629ef9a4dbbd24dfec encoding/positive/tstr_ietf.cbor +ba28ef78acf377aa5ddb54b6e629a918180406927ee5cdbcc6eaf7384458076ca4a79a456a8e268c0fcb709f424b0b4d encoding/positive/tstr_ietf.desc.json +ad6343fe124b30565296d49d57c0e46565819a5aa721a99890c4872208aeac2ece9a3584e0b7fb0e3e54e0396af40085 encoding/positive/tstr_ietf.diag +1d6731ed53d630d73c9ec9fbb6ed0db33f1cc5d319f0341261245dfe39d7cf3ae844cdccb16f845ae75588e8510073cf encoding/positive/tstr_quote_backslash.cbor +290433236a7d2ebbbeefe51c1770aea3d3ea448eb1ea3a456d5cd2239d15957ce21244215452856d8a594a00b0132c47 encoding/positive/tstr_quote_backslash.desc.json +c6d3750ca86c4d818837bd6eb3c68f74cc04f011849b523a05846e9c172e6e748a682c14b6d381a07ec259544c07c112 encoding/positive/tstr_quote_backslash.diag +e59de188bd8c95ea42f9e9b92c95e353045212163a6aac3e7bec299b54d92a764db01e9da68271b5b96e69484920d5d4 encoding/positive/tstr_u_umlaut.cbor +92640610b1e6ddf7d23e14f9e292684e0d95e1a58c17d394b3003ddd14bb91aec02e310c4dc75319f444201653cc7453 encoding/positive/tstr_u_umlaut.desc.json +f14e23e0f57616899fbcab8f4ddf67abeeecc2974874baa56d7eb5df4576d0e6e6c35a03589b939146702cd257d953cf encoding/positive/tstr_u_umlaut.diag +5c63527eb568603d953c29a9372d55c9f05e339cc963487a222e270a166c446b997f02a5745a4f0f5910fa4ecac194ba encoding/positive/tstr_water.cbor +70969ea9884efbe092befa205e839cb3f64a34aaae9f1383ec94891b51b3be27ecea1592bb530701359eed738c2f2fe5 encoding/positive/tstr_water.desc.json +20c1f02901b5951bf0e1fae864b8946305f1d3f9a507d5e97d488b69de964883269e365c77bb80ed0102b30106d93e3d encoding/positive/tstr_water.diag +bec021b4f368e3069134e012c2b4307083d3a9bdd206e24e5f0d86e13d6636655933ec2b413465966817a9c208a11717 encoding/positive/uint_0.cbor +b9485aef4ca5221d2a57bf9b4fbd94714b614258b9e7860fb7f298f200ac4a98dedbae632b8861b220f8ee97ad721155 encoding/positive/uint_0.desc.json +c0210b6eb9e16e86f212ff5bbe7cf2e7259eba652916602a9dbc8d3a65e199d0e902f355de2bd48d87fda1d7f31bedb7 encoding/positive/uint_0.diag +8d2ce87d86f55fcfab770a047b090da23270fa206832dfea7e0c946fff451f819add242374be551b0d6318ed6c7d41d8 encoding/positive/uint_1.cbor +0f919998aa8b63b752ef94eac7af0f815b3cbe054304f4b19b40c99af33a14ba16e1d8ca5258afe934670322b150a230 encoding/positive/uint_1.desc.json +d654902b550e334bb6898d5c4ab8ebe1aedc6c85368eafe28e0f89b62a74a23e1ed20abbc10c02ce321266384d444717 encoding/positive/uint_1.diag +ec664e889ed6c1b2763cacf7899d95b7f347373eb982e523419feea3aa362d891b3bf025f292267a5854049091789c3e encoding/positive/uint_10.cbor +14f845312c284073c9d2046a2122625f17edf9400633c05475c17b6fcdab30198030fc53888c1492240562cb069c4817 encoding/positive/uint_10.desc.json +603638ffc3ccbf902c1ab7d990ce1b24a84b7c2b50fb21efdda6366882dc82b70a12ad378647b79e94eefb10ded11b47 encoding/positive/uint_10.diag +6ea03c5a94315a93043fe257006f952781ecc0ff72a8b30577a74a06bab1b295412a12fa6d48f854785861dd61227af3 encoding/positive/uint_100.cbor +dd9679e0fe265002bdd9bd67fdfa2d355074e527582623eb29fdf0f629ed77ee924fc53fd148fc636ce5032f30ddb3ff encoding/positive/uint_100.desc.json +98cb0ecf881e4d730622da19e6213795786cf0ad9c4deb94e2c3140c8b209b9b9a7f31c62e0a2f48a42bc7319a835928 encoding/positive/uint_100.diag +9c05f1a74bbcd6377beb819c1949ae8841f597c76544ad348762aa93c0bae2b7cb65aa065c079c35aec07cd1285955ba encoding/positive/uint_1000.cbor +d33b59c3e4dad455c570fd9535711a6ad2d341941341151d3eeb1343c253518219e832568be1436eb8d74628c54b3b03 encoding/positive/uint_1000.desc.json +78ab7b515f2aac54032c4d4cc3ac266e1632074d59bb9616a622f542d3ba0d1c9911b30b2d90da641057e8347c4e88ac encoding/positive/uint_1000.diag +c694409ab656b5b783ed9787716dff325dee8991744b4012cbc7c10a208dbf65c60805815681d58d26ef1047a05df549 encoding/positive/uint_1000000.cbor +b0455797f27301c809b4ef7049e945cf913e2c139cf55e597776f308719587d84c18b095fe6fa441ad6878e397f3a8cb encoding/positive/uint_1000000.desc.json +aa4b4d1ef573e58e3608157bdfb35c940820ddb3b14c75a5ce698eafa1c0fd9bfd44f2341be9bb34601269198e819baf encoding/positive/uint_1000000.diag +8767d6e46e45db24f9eb5a661803a01941c2bc0ded16a29093ecdb00fb459e7b9d7a851fd79d56da554358d631c89d56 encoding/positive/uint_1000000000000.cbor +fd73d2042fe0b7b5f7e6449648c8e6ee6da14d3646861085c9a952c78c3e781c3188bdcd1c868fbfb5787c1aaf75058d encoding/positive/uint_1000000000000.desc.json +a83379736c0245c89927cc846fb93bbc052ec89df3581b413247a164384678d15bf0572bad6c7acbaf955cd4d64e07ae encoding/positive/uint_1000000000000.diag +e9d93fbaf0a1b2a426fa5deb8ddfda2a79ea8bc59de5617c12614727fe75973a18ff5bcfa20c4b001af77d64771c31ba encoding/positive/uint_23.cbor +767e3417d0c9e3ad5e8436955b7830643221f605468f40e5a74391a69680fe08b72455243eaa8c9b84b03019d26c4cd2 encoding/positive/uint_23.desc.json +c1fa4e0220475d86921f384d0d7280f859ec611984856edcfe25424383b81141ce67d704a127895e355c092aa3e28d0c encoding/positive/uint_23.diag +36e5d1f4342895f57a3baa9af5369a58bc69f682597f7161b09e09ccb435c448f087e33211c0a206c60f459052ad2399 encoding/positive/uint_24.cbor +b10021f9e5cbfa6ec91f964a98cc0749e29a43b7260bc6e81c53ee31be011f731fc6eff82f6d5811946c31c4e85471e6 encoding/positive/uint_24.desc.json +07895ede6456caf3f4840b224b883f97cfc80525fd0a6e48cec9a043cf26c50f376d80cfee24d2f43fdd2e07cfcc4b91 encoding/positive/uint_24.diag +d6cbe8ef066e6b4854d05c27b1e30aebba2d2abf201b6a749a73a6ca84aeea0dad94461604eb7f4de6c8456549b9e55b encoding/positive/uint_25.cbor +926c2476b03809efa4edfc7df646d1d4c670d76d31a03fbf63dc5eee5dce8cbe0397f80b1de5ed7da32604ecc54b00c4 encoding/positive/uint_25.desc.json +8e70de24cebc3f746f54231785347a3b3a0da4a1e59d207fa2674f846a5982da75f2fa9003d3b8c868104bd04d2d4f1d encoding/positive/uint_25.diag +de4e7e40c458ceced2c74a9d13babb237eaf73e27e3e6fc41739b4f412507a27f582822f743b9a6ed478221a32fb1b29 encoding/positive/uint_255.cbor +cd651b6a79fd78235fbee122ef6d7c033ffd8651ef784c22410802fa59130b3919158e02c7156a774a1a987680f2a266 encoding/positive/uint_255.desc.json +bdb1b2710245928cd1bbb9995b428852054f0d4f4bcf4982fddce5e3a2ba0d89bba6c5bad1a938dd9bb160dee3a5104a encoding/positive/uint_255.diag +129b5b4a2de3a08856fc27108d755c8d4b55edb7805766f54e0273f6cde6df8437cb6cf87d1f716747a4738456c2197f encoding/positive/uint_256.cbor +afcb739712c3bd20fcf08dd25a174ebbbabf2e46e4ae865884bda8302fe54514b0e3c20e79087d590729a44ed77473d4 encoding/positive/uint_256.desc.json +5ec2508e81ca99358c173a0f3abbee0794143df5f2f2ff7fa843a3a87d48932e20316bb89c42a32c623ac8da21450ea7 encoding/positive/uint_256.diag +e087a78d563fb9810b0ba7018c58414c80c6de22000664cfe10e61cd8456b9b16152dc5a8a28d471ee828e883b02b388 encoding/positive/uint_4294967295.cbor +56ffbc7d7391e3a4789e0d5d5e481b0527f7ed45b87a6847c27e39dc8f76d210b3f0ec823fc46e9ed963e35ef1bf1ddc encoding/positive/uint_4294967295.desc.json +7406359633e8904146bb5d62a8aa4515565c32c2495952a65cb8f15091d98a03497a757f8a93f9b18ae7079fb0069716 encoding/positive/uint_4294967295.diag +f5441885a35767ffd70da892d84c3ade42edbef50bffff0a58093aaffded1bb1738903890ebc88aeac90609fda2d6774 encoding/positive/uint_4294967296.cbor +e2167585e5e97b13fe961eef4b12e9c82db6191dc11226f2acf204106928021e51162afd69dfae5e78fac05c268ef619 encoding/positive/uint_4294967296.desc.json +5f80737c9bbcfdf18585f170d346aa5fac7f009761d488c48b3aa718e65d41160e610c3f474c3e0f9da726aa37f547e2 encoding/positive/uint_4294967296.diag +6e37683854cb5ef220eba064092303d812fafb6501485a99f9c7598f3357d537a6fd8275c30206810acde93f3b1747a0 encoding/positive/uint_65535.cbor +75dfcd6e18d1a524c309c50856616a8e54b72c2bfcc703d7709ae81e39f4cc868bdfcb30c44dcf3a936939ed9b3e2dc7 encoding/positive/uint_65535.desc.json +b2658c347dc6ffab95c3c8743c011cfc3a96e332d271a003ff178b25dcd7498f19ea032942680e31a9fb2d71bec6a232 encoding/positive/uint_65535.diag +c5e89d8b6fc39ef1a7e490cf84dd9af2fb1f6ad47db4ad756129bdd8c31fbe7938113e4a8b4d81bab17400ce38e370a6 encoding/positive/uint_65536.cbor +b35433a1e93042b7800388a8cdaacc0c4d687eaf2c732e97833d2fbfcf727e4842dcf11c79d1238761778593874803ab encoding/positive/uint_65536.desc.json +21213bdd3f450557a6f0b49f2b1b3787c7069a7c3870e61747c4e0ffbf9f2feb431bc6770ec9d259212006b4089a6e49 encoding/positive/uint_65536.diag +9fc3e9ec47a5e34c1f8b74e0321384d27f2b0ea95893505b447f01d0f4792b9162593208b79c86bb40edf44bd4ff451c encoding/positive/uint_max_u64.cbor +27017d84a5a12945ff935829a478dcde1386db298b93c1c490e84f62714d7c1b3c146639359e56c56f0d8bbbfd982262 encoding/positive/uint_max_u64.desc.json +f116bf4ce82cb0cd46d91e5ff2442b3f527eae8affefcef0678d2e37980babed66a01689a4d2bc0fa36195601a83f02d encoding/positive/uint_max_u64.diag +38999b3fb39414528cb2bf618aa60979755e666a2a2358feb1796694ea50c50319c90b6cdc5d2e5085d7984e0e9153c6 header/negative/alg_missing/no_label_1.cbor +c9e09f8bf67b2857043cd7029d06f50b97a0f28946f1e8d7c4022d27b43262f5a389c1c07be090a5528051b3cdd0f879 header/negative/alg_missing/no_label_1.expect +78dffd69b0ef23c3c594da067e7de10004866506d28f3ff8059883f777e03deef713f1a0ebc9b1a51ea3daf8fea972ca header/negative/alg_not_mldsa87/eddsa.cbor +2cae37aab4a8bdaa238c2f9042125963bc7041226923d3bdc6448e90a421b6893269729c154024f8cc4bb9b28cd9b2ff header/negative/alg_not_mldsa87/eddsa.expect +f158dcfe0b056f6ba777f297fa4de874c310130a13724c67fa0b098f585dc3635cd9a8ec9c2d5d389f1b4b6e48561087 header/negative/alg_not_mldsa87/mldsa65.cbor +2cae37aab4a8bdaa238c2f9042125963bc7041226923d3bdc6448e90a421b6893269729c154024f8cc4bb9b28cd9b2ff header/negative/alg_not_mldsa87/mldsa65.expect +c1d2bca00f5d6a4559f83d346d955b2dfcedb3d446c9f98b946f556e690f6bf52b43264961f9a9a29ff4b5cec6c28cc0 header/negative/content_type/label_3.cbor +6a34cc3f009852f3d9152b50767d87ec7118c35f3250bac96f6b6e23ba2b0da7e7d253af221975341459fbb3eba882ea header/negative/content_type/label_3.expect +6c7b67126133d4c6d4e19dab32e6c1cc867d327eed0fe3495619c174c335c8ea0a750f41bf71808196ee152b0eb181a9 header/negative/crit/label_2.cbor +de8993fcf43d057b9fb9c9e8228988221b3b1b278a9446bf6a8cd248cd91b1a1edcf2ff6dcd27620a7639260bb89b3e3 header/negative/crit/label_2.expect +069b12f677a24da04e0817197e56df948d9018b7a7e58033f6d226d40ce5f602bbff9aff3b51dd40f731559e558c4675 header/negative/cwt_without_scitt_typ/cwt_on_plain_typ.cbor +de8993fcf43d057b9fb9c9e8228988221b3b1b278a9446bf6a8cd248cd91b1a1edcf2ff6dcd27620a7639260bb89b3e3 header/negative/cwt_without_scitt_typ/cwt_on_plain_typ.expect +7922b4bde2f6953b729fc77ac6a951a6ac0df9baf01bfd9ed4391feb28d1d0b6c9467d386c74dfafe1aeb1d1640b138a header/negative/header_noncanonical/nonminimal_label.cbor +b3da2ed7fe90d8e0aaaf139e44bdc0eb2f73a17e6ca9e3bc6195eb00f367de530e4350787e13168ca9476f57cbc74c21 header/negative/header_noncanonical/nonminimal_label.expect +bea745bd25ab1a19cef9371198034998396dcd433e6944c34e47d826cdd8c6bafbc7ff0d0ba873d26a6cf6c9c08a14df header/negative/header_noncanonical/trailing_byte.cbor +b3da2ed7fe90d8e0aaaf139e44bdc0eb2f73a17e6ca9e3bc6195eb00f367de530e4350787e13168ca9476f57cbc74c21 header/negative/header_noncanonical/trailing_byte.expect +bedee35e886d43100940c40445fd69e0723deae6eaabad1a290978679a9dea928581f8e5ff97f4b6e4235eeeb1fb8794 header/negative/header_noncanonical/typ_before_kid.cbor +b3da2ed7fe90d8e0aaaf139e44bdc0eb2f73a17e6ca9e3bc6195eb00f367de530e4350787e13168ca9476f57cbc74c21 header/negative/header_noncanonical/typ_before_kid.expect +baa5c58aef7d99eef595d893549700b6996af49a0924a9b7742973f923f979acc5c24a6e4ad9ddf0b181bd8ad1997cbc header/negative/kid_missing/no_label_4.cbor +c9e09f8bf67b2857043cd7029d06f50b97a0f28946f1e8d7c4022d27b43262f5a389c1c07be090a5528051b3cdd0f879 header/negative/kid_missing/no_label_4.expect +9994e77c0c4ea9a251f19734bf72e8635b603ee5b3c622497384b67f48f6e559a9a26cb7225a66b8e087ed4cbbc5a869 header/negative/kid_wrong_size/kid_32.cbor +c0e55c7e23b1f3a1e5a57f2d31554a4d2974b97b62526b78b52515cec531269f84f3a740aa21dc30bc19a9039903ac98 header/negative/kid_wrong_size/kid_32.expect +3a2638d24852775a96fb797693da94828c9650370723b568875321d65e0a9ea94d29f80148e016811aba8358fe7246fe header/negative/label_0/label_0.cbor +de8993fcf43d057b9fb9c9e8228988221b3b1b278a9446bf6a8cd248cd91b1a1edcf2ff6dcd27620a7639260bb89b3e3 header/negative/label_0/label_0.expect +c2c07dae5d498d19f870cd18232ce1b42bff11b0cb42aea7e5b8f8dbcce5f246693b83589322eac202e10dece1cea9cf header/negative/label_5/iv.cbor +de8993fcf43d057b9fb9c9e8228988221b3b1b278a9446bf6a8cd248cd91b1a1edcf2ff6dcd27620a7639260bb89b3e3 header/negative/label_5/iv.expect +ef54d0bfeb9c1a6936c18d3d025f47ba197de6c4b3ca0d6c38b5a8c11f54dfb2382a27692e1042a6ee438e413c4f3c17 header/negative/label_hash_envelope/label_258.cbor +de8993fcf43d057b9fb9c9e8228988221b3b1b278a9446bf6a8cd248cd91b1a1edcf2ff6dcd27620a7639260bb89b3e3 header/negative/label_hash_envelope/label_258.expect +e9df93fb9e0bb188e5905912af1d29913465d8d07b67824b0d777cc48fb237ae910e20e5fba704a10ddba259953d2f3c header/negative/label_negative/minus_1.cbor +de8993fcf43d057b9fb9c9e8228988221b3b1b278a9446bf6a8cd248cd91b1a1edcf2ff6dcd27620a7639260bb89b3e3 header/negative/label_negative/minus_1.expect +33542ee98e2606188f3fde1f8bb5b2bd82ba4f98704401500807456b3e2dbd5feddab6dd102c72b1e9f1cc87fa38df08 header/negative/typ_missing/no_label_16.cbor +c9e09f8bf67b2857043cd7029d06f50b97a0f28946f1e8d7c4022d27b43262f5a389c1c07be090a5528051b3cdd0f879 header/negative/typ_missing/no_label_16.expect +f793ff73ad9315e95f55067c334dac8c0fe72463c58953108aaa18cb59aec15f751209b73031e5843ab69c73254a589a header/negative/typ_not_in_table/other.cbor +c38c1bb721e5f1c4f62dd9b2a6db895af7350fbd905c5609ef068bc54b87f9453e80c26cb9574f86fe32c34dc5f4f88c header/negative/typ_not_in_table/other.expect +7c51407ae8602664a3dafd9ff25dab31045c4a517beed56e5711c18bf6ad024bf378f92cbad9bb89ec95d8a56b9868d1 header/negative/typ_uint/typ_1.cbor +c38c1bb721e5f1c4f62dd9b2a6db895af7350fbd905c5609ef068bc54b87f9453e80c26cb9574f86fe32c34dc5f4f88c header/negative/typ_uint/typ_1.expect +d5da8e2d7de0437d378fbb09fac16fc4a1e073e1afc704648b66555623c86d73840241553dfc9e722cae66dccb9619f8 header/positive/alg_kid_cwt_typ_scitt.cbor +a9b5e6706fec09c6f59343ef85022f91767b3939965b27a3053852638968d916b7b9142c315f241d6155171c7a4f0f98 header/positive/alg_kid_typ.cbor +4e833218fa3384f4ca4ef6d9d86c79fbdb36f24a0c1379027485ee8fb804c7fd8f05e6ca6a3820b5f9b3d7b88707c2b2 header/profile-table.json +aab9ecd6c7320915435631cae66cf8bc25a4c33d782c8fe5f81a55eb40e3df2d81450d101f068d52bed4f37a2ec0492e thumbprint/acvp_mldsa87_tg3_tc51.kid +954bc653b6b8f323b050607cb2db6560e0a6634ec020083892bfb7c2cd4370b9b3b44161739b9cc9a6a4d56ac17d3bc2 thumbprint/acvp_mldsa87_tg3_tc51.pub diff --git a/test/fixtures/n6/VERSION b/test/fixtures/n6/VERSION new file mode 100644 index 0000000..3eeddaa --- /dev/null +++ b/test/fixtures/n6/VERSION @@ -0,0 +1 @@ +n6/v0 draft r3 PROVISIONAL (Trinity candidate set) diff --git a/test/fixtures/n6/body/negative/bool_schema_version/true.cbor b/test/fixtures/n6/body/negative/bool_schema_version/true.cbor new file mode 100644 index 0000000000000000000000000000000000000000..ad00f91d4cb875597ee4e3d48d8c46fe7aac1ceb GIT binary patch literal 6 NcmZ3)@KqqO0ssjR0)YSk literal 0 HcmV?d00001 diff --git a/test/fixtures/n6/body/negative/bool_schema_version/true.expect b/test/fixtures/n6/body/negative/bool_schema_version/true.expect new file mode 100644 index 0000000..bfc8da5 --- /dev/null +++ b/test/fixtures/n6/body/negative/bool_schema_version/true.expect @@ -0,0 +1 @@ +{"exit":1,"source":"hand","verdict_code":"key_not_uint"} diff --git a/test/fixtures/n6/body/negative/carrier_schema_key0/envelope_schema.cbor b/test/fixtures/n6/body/negative/carrier_schema_key0/envelope_schema.cbor new file mode 100644 index 0000000000000000000000000000000000000000..22fca3514cc1e486cd0e63570a9574f7325f8e7a GIT binary patch literal 10 RcmZ3)Aoc%01EWA<1ppO~1El}} literal 0 HcmV?d00001 diff --git a/test/fixtures/n6/body/negative/carrier_schema_key0/envelope_schema.expect b/test/fixtures/n6/body/negative/carrier_schema_key0/envelope_schema.expect new file mode 100644 index 0000000..8b66e01 --- /dev/null +++ b/test/fixtures/n6/body/negative/carrier_schema_key0/envelope_schema.expect @@ -0,0 +1 @@ +{"exit":1,"source":"hand","verdict_code":"schema_reserved"} diff --git a/test/fixtures/n6/body/negative/carrier_schema_key0/top_of_range.cbor b/test/fixtures/n6/body/negative/carrier_schema_key0/top_of_range.cbor new file mode 100644 index 0000000000000000000000000000000000000000..bb74397f5b9c3dbdae694955aefc7fa3dea2d6a6 GIT binary patch literal 10 PcmZ3)AoU*z1QII%9)bm; literal 0 HcmV?d00001 diff --git a/test/fixtures/n6/body/negative/carrier_schema_key0/top_of_range.expect b/test/fixtures/n6/body/negative/carrier_schema_key0/top_of_range.expect new file mode 100644 index 0000000..8b66e01 --- /dev/null +++ b/test/fixtures/n6/body/negative/carrier_schema_key0/top_of_range.expect @@ -0,0 +1 @@ +{"exit":1,"source":"hand","verdict_code":"schema_reserved"} diff --git a/test/fixtures/n6/body/negative/negative_schema_version/minus_1.cbor b/test/fixtures/n6/body/negative/negative_schema_version/minus_1.cbor new file mode 100644 index 0000000000000000000000000000000000000000..fc398ca72b5d02b1d7514871cae71a345b4e19ae GIT binary patch literal 6 NcmZ3)pdgS~0RRQO0jvN3 literal 0 HcmV?d00001 diff --git a/test/fixtures/n6/body/negative/negative_schema_version/minus_1.expect b/test/fixtures/n6/body/negative/negative_schema_version/minus_1.expect new file mode 100644 index 0000000..bfc8da5 --- /dev/null +++ b/test/fixtures/n6/body/negative/negative_schema_version/minus_1.expect @@ -0,0 +1 @@ +{"exit":1,"source":"hand","verdict_code":"key_not_uint"} diff --git a/test/fixtures/n6/body/negative/no_schema_version/no_key_0.cbor b/test/fixtures/n6/body/negative/no_schema_version/no_key_0.cbor new file mode 100644 index 0000000..451f06e --- /dev/null +++ b/test/fixtures/n6/body/negative/no_schema_version/no_key_0.cbor @@ -0,0 +1 @@ +¡ax \ No newline at end of file diff --git a/test/fixtures/n6/body/negative/no_schema_version/no_key_0.expect b/test/fixtures/n6/body/negative/no_schema_version/no_key_0.expect new file mode 100644 index 0000000..91424f1 --- /dev/null +++ b/test/fixtures/n6/body/negative/no_schema_version/no_key_0.expect @@ -0,0 +1 @@ +{"exit":1,"source":"hand","verdict_code":"schema_version_missing"} diff --git a/test/fixtures/n6/body/negative/not_a_map/array.cbor b/test/fixtures/n6/body/negative/not_a_map/array.cbor new file mode 100644 index 0000000000000000000000000000000000000000..e1707aeae4b52ed59e568a0a041696cdd36b71b1 GIT binary patch literal 3 KcmZo-U<3dGiU5QF literal 0 HcmV?d00001 diff --git a/test/fixtures/n6/body/negative/not_a_map/array.expect b/test/fixtures/n6/body/negative/not_a_map/array.expect new file mode 100644 index 0000000..993db41 --- /dev/null +++ b/test/fixtures/n6/body/negative/not_a_map/array.expect @@ -0,0 +1 @@ +{"exit":1,"source":"hand","verdict_code":"body_not_map"} diff --git a/test/fixtures/n6/body/negative/required_key_missing/no_name.cbor b/test/fixtures/n6/body/negative/required_key_missing/no_name.cbor new file mode 100644 index 0000000000000000000000000000000000000000..4a34bafa5a61746dc358765e3614c757a1d325ee GIT binary patch literal 3 KcmZ3;zz6^V=K!Ms literal 0 HcmV?d00001 diff --git a/test/fixtures/n6/body/negative/required_key_missing/no_name.expect b/test/fixtures/n6/body/negative/required_key_missing/no_name.expect new file mode 100644 index 0000000..f57b350 --- /dev/null +++ b/test/fixtures/n6/body/negative/required_key_missing/no_name.expect @@ -0,0 +1 @@ +{"exit":1,"source":"hand","verdict_code":"required_key_missing"} diff --git a/test/fixtures/n6/body/negative/set_duplicate/1_twice.cbor b/test/fixtures/n6/body/negative/set_duplicate/1_twice.cbor new file mode 100644 index 0000000000000000000000000000000000000000..e93efbc2908ea8aff86274f9a988a474dfa160e7 GIT binary patch literal 10 RcmZ3?z$lPdA=Jdk2mlRF0we$c literal 0 HcmV?d00001 diff --git a/test/fixtures/n6/body/negative/set_duplicate/1_twice.expect b/test/fixtures/n6/body/negative/set_duplicate/1_twice.expect new file mode 100644 index 0000000..46cc59b --- /dev/null +++ b/test/fixtures/n6/body/negative/set_duplicate/1_twice.expect @@ -0,0 +1 @@ +{"exit":1,"source":"hand","verdict_code":"set_not_canonical"} diff --git a/test/fixtures/n6/body/negative/set_unsorted/2_then_1.cbor b/test/fixtures/n6/body/negative/set_unsorted/2_then_1.cbor new file mode 100644 index 0000000000000000000000000000000000000000..aa3c625dde91126f71139e77e986d06bb93b7076 GIT binary patch literal 10 RcmZ3?z$lPdA=Jdg2mlRH0wn+d literal 0 HcmV?d00001 diff --git a/test/fixtures/n6/body/negative/set_unsorted/2_then_1.expect b/test/fixtures/n6/body/negative/set_unsorted/2_then_1.expect new file mode 100644 index 0000000..46cc59b --- /dev/null +++ b/test/fixtures/n6/body/negative/set_unsorted/2_then_1.expect @@ -0,0 +1 @@ +{"exit":1,"source":"hand","verdict_code":"set_not_canonical"} diff --git a/test/fixtures/n6/body/negative/set_unsorted/longer_head_first.cbor b/test/fixtures/n6/body/negative/set_unsorted/longer_head_first.cbor new file mode 100644 index 0000000000000000000000000000000000000000..8138082d1b8a7cde453820ec18d3f20e8b8f681d GIT binary patch literal 11 ScmZ3?z$lPdA=D%xAr1f!{Q_wK literal 0 HcmV?d00001 diff --git a/test/fixtures/n6/body/negative/set_unsorted/longer_head_first.expect b/test/fixtures/n6/body/negative/set_unsorted/longer_head_first.expect new file mode 100644 index 0000000..46cc59b --- /dev/null +++ b/test/fixtures/n6/body/negative/set_unsorted/longer_head_first.expect @@ -0,0 +1 @@ +{"exit":1,"source":"hand","verdict_code":"set_not_canonical"} diff --git a/test/fixtures/n6/body/negative/unknown_key/key_19.cbor b/test/fixtures/n6/body/negative/unknown_key/key_19.cbor new file mode 100644 index 0000000000000000000000000000000000000000..fec46aed7bfe7157a0b093e175a97075d9fb298b GIT binary patch literal 8 PcmZ3?z$lPdAD!%*ezH024X_KmY&$ literal 0 HcmV?d00001 diff --git a/test/fixtures/n6/body/positive/all_fields.desc.json b/test/fixtures/n6/body/positive/all_fields.desc.json new file mode 100644 index 0000000..22fa923 --- /dev/null +++ b/test/fixtures/n6/body/positive/all_fields.desc.json @@ -0,0 +1 @@ +{"source":"hand","value":{"m":[[{"i":"0"},{"i":"1"}],[{"i":"16"},{"t":"78"}],[{"i":"17"},{"i":"5"}],[{"i":"18"},{"a":[{"i":"1"},{"i":"2"},{"i":"3"}]}]]}} diff --git a/test/fixtures/n6/body/positive/all_fields.diag b/test/fixtures/n6/body/positive/all_fields.diag new file mode 100644 index 0000000..029e8f4 --- /dev/null +++ b/test/fixtures/n6/body/positive/all_fields.diag @@ -0,0 +1 @@ +{0: 1, 16: "x", 17: 5, 18: [1, 2, 3]} diff --git a/test/fixtures/n6/body/positive/minimal.cbor b/test/fixtures/n6/body/positive/minimal.cbor new file mode 100644 index 0000000000000000000000000000000000000000..9770d44ae72c9a8bbbfae17cf6e92217d443db04 GIT binary patch literal 6 NcmZ3)z$lPd0RRO+0gV6v literal 0 HcmV?d00001 diff --git a/test/fixtures/n6/body/positive/minimal.desc.json b/test/fixtures/n6/body/positive/minimal.desc.json new file mode 100644 index 0000000..cf71aa4 --- /dev/null +++ b/test/fixtures/n6/body/positive/minimal.desc.json @@ -0,0 +1 @@ +{"source":"hand","value":{"m":[[{"i":"0"},{"i":"1"}],[{"i":"16"},{"t":"78"}]]}} diff --git a/test/fixtures/n6/body/positive/minimal.diag b/test/fixtures/n6/body/positive/minimal.diag new file mode 100644 index 0000000..e5b23c1 --- /dev/null +++ b/test/fixtures/n6/body/positive/minimal.diag @@ -0,0 +1 @@ +{0: 1, 16: "x"} diff --git a/test/fixtures/n6/body/positive/set_sorted_by_encoded_bytes.cbor b/test/fixtures/n6/body/positive/set_sorted_by_encoded_bytes.cbor new file mode 100644 index 0000000000000000000000000000000000000000..486afdd9441064cbc478670273dd8c746a818bce GIT binary patch literal 14 VcmZ3?z$lPdA=E4`AtA}g000+?0)zkn literal 0 HcmV?d00001 diff --git a/test/fixtures/n6/body/positive/set_sorted_by_encoded_bytes.desc.json b/test/fixtures/n6/body/positive/set_sorted_by_encoded_bytes.desc.json new file mode 100644 index 0000000..3a21bcd --- /dev/null +++ b/test/fixtures/n6/body/positive/set_sorted_by_encoded_bytes.desc.json @@ -0,0 +1 @@ +{"source":"hand","value":{"m":[[{"i":"0"},{"i":"1"}],[{"i":"16"},{"t":"78"}],[{"i":"18"},{"a":[{"i":"23"},{"i":"24"},{"i":"256"}]}]]}} diff --git a/test/fixtures/n6/body/positive/set_sorted_by_encoded_bytes.diag b/test/fixtures/n6/body/positive/set_sorted_by_encoded_bytes.diag new file mode 100644 index 0000000..7811752 --- /dev/null +++ b/test/fixtures/n6/body/positive/set_sorted_by_encoded_bytes.diag @@ -0,0 +1 @@ +{0: 1, 16: "x", 18: [23, 24, 256]} diff --git a/test/fixtures/n6/body/schema.json b/test/fixtures/n6/body/schema.json new file mode 100644 index 0000000..6f0b18c --- /dev/null +++ b/test/fixtures/n6/body/schema.json @@ -0,0 +1 @@ +{"fields":[{"key":16,"name":"name","required":true,"type":"tstr"},{"key":17,"name":"count","required":false,"type":"uint"},{"key":18,"name":"members","required":false,"type":"set of uint"}],"kind":"trinity.test","note":"Trinity's test schema for the body vectors. The draft leaves test schemas Open (VB6); this one is Trinity's.","provisional":true,"schema_version":1} diff --git a/test/fixtures/n6/digest/abc.in b/test/fixtures/n6/digest/abc.in new file mode 100644 index 0000000..f2ba8f8 --- /dev/null +++ b/test/fixtures/n6/digest/abc.in @@ -0,0 +1 @@ +abc \ No newline at end of file diff --git a/test/fixtures/n6/digest/abc.ref.cbor b/test/fixtures/n6/digest/abc.ref.cbor new file mode 100644 index 0000000000000000000000000000000000000000..5f39a76023426f8b4134d8cf850e46221af3b878 GIT binary patch literal 84 zcmV-a0IUC_0D?FwSTM@~bw5R;UW>J$J!zW8PzNV0GOG^W(PJ8mU|K`}TkU{`2P>iH q%p+L3?4dNsC8q%c0(clH&BF literal 0 HcmV?d00001 diff --git a/test/fixtures/n6/encoding/negative/nan/half.expect b/test/fixtures/n6/encoding/negative/nan/half.expect new file mode 100644 index 0000000..c7aa969 --- /dev/null +++ b/test/fixtures/n6/encoding/negative/nan/half.expect @@ -0,0 +1 @@ +{"exit":1,"source":"RFC 8949 Appendix A","verdict_code":"float_forbidden"} diff --git a/test/fixtures/n6/encoding/negative/nan/single.cbor b/test/fixtures/n6/encoding/negative/nan/single.cbor new file mode 100644 index 0000000000000000000000000000000000000000..2a9ccaec94adda68dea3be3ff87c02583d08f491 GIT binary patch literal 5 McmexWe}I7j00|=kIsgCw literal 0 HcmV?d00001 diff --git a/test/fixtures/n6/encoding/negative/nan/single.expect b/test/fixtures/n6/encoding/negative/nan/single.expect new file mode 100644 index 0000000..c7aa969 --- /dev/null +++ b/test/fixtures/n6/encoding/negative/nan/single.expect @@ -0,0 +1 @@ +{"exit":1,"source":"RFC 8949 Appendix A","verdict_code":"float_forbidden"} diff --git a/test/fixtures/n6/encoding/negative/negative_map_key/minus_1.cbor b/test/fixtures/n6/encoding/negative/negative_map_key/minus_1.cbor new file mode 100644 index 0000000000000000000000000000000000000000..d9a9e045654ec9d4e2f48c2449f04d6f65f74149 GIT binary patch literal 3 KcmZ3ezyJUOCIG?! literal 0 HcmV?d00001 diff --git a/test/fixtures/n6/encoding/negative/negative_map_key/minus_1.expect b/test/fixtures/n6/encoding/negative/negative_map_key/minus_1.expect new file mode 100644 index 0000000..bfc8da5 --- /dev/null +++ b/test/fixtures/n6/encoding/negative/negative_map_key/minus_1.expect @@ -0,0 +1 @@ +{"exit":1,"source":"hand","verdict_code":"key_not_uint"} diff --git a/test/fixtures/n6/encoding/negative/nested_text_key/in_array.cbor b/test/fixtures/n6/encoding/negative/nested_text_key/in_array.cbor new file mode 100644 index 0000000..9b1d08c --- /dev/null +++ b/test/fixtures/n6/encoding/negative/nested_text_key/in_array.cbor @@ -0,0 +1 @@ +‚aa¡abac \ No newline at end of file diff --git a/test/fixtures/n6/encoding/negative/nested_text_key/in_array.expect b/test/fixtures/n6/encoding/negative/nested_text_key/in_array.expect new file mode 100644 index 0000000..a8acb52 --- /dev/null +++ b/test/fixtures/n6/encoding/negative/nested_text_key/in_array.expect @@ -0,0 +1 @@ +{"exit":1,"source":"RFC 8949 Appendix A","verdict_code":"key_not_uint"} diff --git a/test/fixtures/n6/encoding/negative/nonminimal_head/length_in_two_bytes.cbor b/test/fixtures/n6/encoding/negative/nonminimal_head/length_in_two_bytes.cbor new file mode 100644 index 0000000000000000000000000000000000000000..6e008e9cbd713ac530adab9253895414d78bfc9e GIT binary patch literal 4 Lcma!KU}OLQ0crqS literal 0 HcmV?d00001 diff --git a/test/fixtures/n6/encoding/negative/nonminimal_head/length_in_two_bytes.expect b/test/fixtures/n6/encoding/negative/nonminimal_head/length_in_two_bytes.expect new file mode 100644 index 0000000..46a0d72 --- /dev/null +++ b/test/fixtures/n6/encoding/negative/nonminimal_head/length_in_two_bytes.expect @@ -0,0 +1 @@ +{"exit":1,"source":"hand","verdict_code":"noncanonical"} diff --git a/test/fixtures/n6/encoding/negative/nonminimal_head/nested_in_array.cbor b/test/fixtures/n6/encoding/negative/nonminimal_head/nested_in_array.cbor new file mode 100644 index 0000000..c9d31e3 --- /dev/null +++ b/test/fixtures/n6/encoding/negative/nonminimal_head/nested_in_array.cbor @@ -0,0 +1 @@ +� \ No newline at end of file diff --git a/test/fixtures/n6/encoding/negative/nonminimal_head/nested_in_array.expect b/test/fixtures/n6/encoding/negative/nonminimal_head/nested_in_array.expect new file mode 100644 index 0000000..46a0d72 --- /dev/null +++ b/test/fixtures/n6/encoding/negative/nonminimal_head/nested_in_array.expect @@ -0,0 +1 @@ +{"exit":1,"source":"hand","verdict_code":"noncanonical"} diff --git a/test/fixtures/n6/encoding/negative/nonminimal_head/uint_0_in_one_byte.cbor b/test/fixtures/n6/encoding/negative/nonminimal_head/uint_0_in_one_byte.cbor new file mode 100644 index 0000000000000000000000000000000000000000..ef2d63ad3c0a4d4152b8b689020734b1b926dc0b GIT binary patch literal 2 Jcmb1O0000o02u%P literal 0 HcmV?d00001 diff --git a/test/fixtures/n6/encoding/negative/nonminimal_head/uint_0_in_one_byte.expect b/test/fixtures/n6/encoding/negative/nonminimal_head/uint_0_in_one_byte.expect new file mode 100644 index 0000000..46a0d72 --- /dev/null +++ b/test/fixtures/n6/encoding/negative/nonminimal_head/uint_0_in_one_byte.expect @@ -0,0 +1 @@ +{"exit":1,"source":"hand","verdict_code":"noncanonical"} diff --git a/test/fixtures/n6/encoding/negative/nonminimal_head/uint_23_in_one_byte.cbor b/test/fixtures/n6/encoding/negative/nonminimal_head/uint_23_in_one_byte.cbor new file mode 100644 index 0000000..c53e6e4 --- /dev/null +++ b/test/fixtures/n6/encoding/negative/nonminimal_head/uint_23_in_one_byte.cbor @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/test/fixtures/n6/encoding/negative/nonminimal_head/uint_23_in_one_byte.expect b/test/fixtures/n6/encoding/negative/nonminimal_head/uint_23_in_one_byte.expect new file mode 100644 index 0000000..46a0d72 --- /dev/null +++ b/test/fixtures/n6/encoding/negative/nonminimal_head/uint_23_in_one_byte.expect @@ -0,0 +1 @@ +{"exit":1,"source":"hand","verdict_code":"noncanonical"} diff --git a/test/fixtures/n6/encoding/negative/nonminimal_head/uint_255_in_two_bytes.cbor b/test/fixtures/n6/encoding/negative/nonminimal_head/uint_255_in_two_bytes.cbor new file mode 100644 index 0000000000000000000000000000000000000000..7c39b2893a94d7ac38b21f26c2f7a4882452cdf7 GIT binary patch literal 3 Kcmb1S_zwU9O#vAI literal 0 HcmV?d00001 diff --git a/test/fixtures/n6/encoding/negative/nonminimal_head/uint_255_in_two_bytes.expect b/test/fixtures/n6/encoding/negative/nonminimal_head/uint_255_in_two_bytes.expect new file mode 100644 index 0000000..46a0d72 --- /dev/null +++ b/test/fixtures/n6/encoding/negative/nonminimal_head/uint_255_in_two_bytes.expect @@ -0,0 +1 @@ +{"exit":1,"source":"hand","verdict_code":"noncanonical"} diff --git a/test/fixtures/n6/encoding/negative/not_well_formed/ai_28.cbor b/test/fixtures/n6/encoding/negative/not_well_formed/ai_28.cbor new file mode 100644 index 0000000..5d33882 --- /dev/null +++ b/test/fixtures/n6/encoding/negative/not_well_formed/ai_28.cbor @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/test/fixtures/n6/encoding/negative/not_well_formed/ai_28.expect b/test/fixtures/n6/encoding/negative/not_well_formed/ai_28.expect new file mode 100644 index 0000000..a5f2739 --- /dev/null +++ b/test/fixtures/n6/encoding/negative/not_well_formed/ai_28.expect @@ -0,0 +1 @@ +{"exit":1,"source":"hand: reserved additional information","verdict_code":"not_well_formed"} diff --git a/test/fixtures/n6/encoding/negative/not_well_formed/ai_30_nint.cbor b/test/fixtures/n6/encoding/negative/not_well_formed/ai_30_nint.cbor new file mode 100644 index 0000000..0817502 --- /dev/null +++ b/test/fixtures/n6/encoding/negative/not_well_formed/ai_30_nint.cbor @@ -0,0 +1 @@ +> \ No newline at end of file diff --git a/test/fixtures/n6/encoding/negative/not_well_formed/ai_30_nint.expect b/test/fixtures/n6/encoding/negative/not_well_formed/ai_30_nint.expect new file mode 100644 index 0000000..96a767c --- /dev/null +++ b/test/fixtures/n6/encoding/negative/not_well_formed/ai_30_nint.expect @@ -0,0 +1 @@ +{"exit":1,"source":"hand","verdict_code":"not_well_formed"} diff --git a/test/fixtures/n6/encoding/negative/not_well_formed/f8_below_32.cbor b/test/fixtures/n6/encoding/negative/not_well_formed/f8_below_32.cbor new file mode 100644 index 0000000..4cab240 --- /dev/null +++ b/test/fixtures/n6/encoding/negative/not_well_formed/f8_below_32.cbor @@ -0,0 +1 @@ +ø \ No newline at end of file diff --git a/test/fixtures/n6/encoding/negative/not_well_formed/f8_below_32.expect b/test/fixtures/n6/encoding/negative/not_well_formed/f8_below_32.expect new file mode 100644 index 0000000..0df2f4b --- /dev/null +++ b/test/fixtures/n6/encoding/negative/not_well_formed/f8_below_32.expect @@ -0,0 +1 @@ +{"exit":1,"source":"hand: RFC 8949 section 3.3","verdict_code":"not_well_formed"} diff --git a/test/fixtures/n6/encoding/negative/not_well_formed/lone_break.cbor b/test/fixtures/n6/encoding/negative/not_well_formed/lone_break.cbor new file mode 100644 index 0000000..ce542ef --- /dev/null +++ b/test/fixtures/n6/encoding/negative/not_well_formed/lone_break.cbor @@ -0,0 +1 @@ +ÿ \ No newline at end of file diff --git a/test/fixtures/n6/encoding/negative/not_well_formed/lone_break.expect b/test/fixtures/n6/encoding/negative/not_well_formed/lone_break.expect new file mode 100644 index 0000000..96a767c --- /dev/null +++ b/test/fixtures/n6/encoding/negative/not_well_formed/lone_break.expect @@ -0,0 +1 @@ +{"exit":1,"source":"hand","verdict_code":"not_well_formed"} diff --git a/test/fixtures/n6/encoding/negative/not_well_formed/uint_ai_31.cbor b/test/fixtures/n6/encoding/negative/not_well_formed/uint_ai_31.cbor new file mode 100644 index 0000000..1d79949 --- /dev/null +++ b/test/fixtures/n6/encoding/negative/not_well_formed/uint_ai_31.cbor @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/test/fixtures/n6/encoding/negative/not_well_formed/uint_ai_31.expect b/test/fixtures/n6/encoding/negative/not_well_formed/uint_ai_31.expect new file mode 100644 index 0000000..96a767c --- /dev/null +++ b/test/fixtures/n6/encoding/negative/not_well_formed/uint_ai_31.expect @@ -0,0 +1 @@ +{"exit":1,"source":"hand","verdict_code":"not_well_formed"} diff --git a/test/fixtures/n6/encoding/negative/simple_16/top.cbor b/test/fixtures/n6/encoding/negative/simple_16/top.cbor new file mode 100644 index 0000000..04f7b5b --- /dev/null +++ b/test/fixtures/n6/encoding/negative/simple_16/top.cbor @@ -0,0 +1 @@ +ð \ No newline at end of file diff --git a/test/fixtures/n6/encoding/negative/simple_16/top.expect b/test/fixtures/n6/encoding/negative/simple_16/top.expect new file mode 100644 index 0000000..174fe69 --- /dev/null +++ b/test/fixtures/n6/encoding/negative/simple_16/top.expect @@ -0,0 +1 @@ +{"exit":1,"source":"RFC 8949 Appendix A","verdict_code":"simple_forbidden"} diff --git a/test/fixtures/n6/encoding/negative/simple_2byte/simple_255.cbor b/test/fixtures/n6/encoding/negative/simple_2byte/simple_255.cbor new file mode 100644 index 0000000..a61a462 --- /dev/null +++ b/test/fixtures/n6/encoding/negative/simple_2byte/simple_255.cbor @@ -0,0 +1 @@ +øÿ \ No newline at end of file diff --git a/test/fixtures/n6/encoding/negative/simple_2byte/simple_255.expect b/test/fixtures/n6/encoding/negative/simple_2byte/simple_255.expect new file mode 100644 index 0000000..174fe69 --- /dev/null +++ b/test/fixtures/n6/encoding/negative/simple_2byte/simple_255.expect @@ -0,0 +1 @@ +{"exit":1,"source":"RFC 8949 Appendix A","verdict_code":"simple_forbidden"} diff --git a/test/fixtures/n6/encoding/negative/simple_2byte/simple_32.cbor b/test/fixtures/n6/encoding/negative/simple_2byte/simple_32.cbor new file mode 100644 index 0000000..4b9d404 --- /dev/null +++ b/test/fixtures/n6/encoding/negative/simple_2byte/simple_32.cbor @@ -0,0 +1 @@ +ø \ No newline at end of file diff --git a/test/fixtures/n6/encoding/negative/simple_2byte/simple_32.expect b/test/fixtures/n6/encoding/negative/simple_2byte/simple_32.expect new file mode 100644 index 0000000..4a407c9 --- /dev/null +++ b/test/fixtures/n6/encoding/negative/simple_2byte/simple_32.expect @@ -0,0 +1 @@ +{"exit":1,"source":"hand","verdict_code":"simple_forbidden"} diff --git a/test/fixtures/n6/encoding/negative/swap_keys/24_then_23.cbor b/test/fixtures/n6/encoding/negative/swap_keys/24_then_23.cbor new file mode 100644 index 0000000000000000000000000000000000000000..6b71ae5248191ff9886bd696eaa6b1e24aee8700 GIT binary patch literal 6 NcmZ1^A;BQd000Eo0O|k$ literal 0 HcmV?d00001 diff --git a/test/fixtures/n6/encoding/negative/swap_keys/24_then_23.expect b/test/fixtures/n6/encoding/negative/swap_keys/24_then_23.expect new file mode 100644 index 0000000..692dd49 --- /dev/null +++ b/test/fixtures/n6/encoding/negative/swap_keys/24_then_23.expect @@ -0,0 +1 @@ +{"exit":1,"source":"hand: a longer head after a shorter key","verdict_code":"noncanonical"} diff --git a/test/fixtures/n6/encoding/negative/swap_keys/2_then_1.cbor b/test/fixtures/n6/encoding/negative/swap_keys/2_then_1.cbor new file mode 100644 index 0000000000000000000000000000000000000000..21f58b5b24e49226374fa6440bad9d07111947f8 GIT binary patch literal 5 McmZ3)#K6b^00TJyrT_o{ literal 0 HcmV?d00001 diff --git a/test/fixtures/n6/encoding/negative/swap_keys/2_then_1.expect b/test/fixtures/n6/encoding/negative/swap_keys/2_then_1.expect new file mode 100644 index 0000000..46a0d72 --- /dev/null +++ b/test/fixtures/n6/encoding/negative/swap_keys/2_then_1.expect @@ -0,0 +1 @@ +{"exit":1,"source":"hand","verdict_code":"noncanonical"} diff --git a/test/fixtures/n6/encoding/negative/tag_1_datetime/epoch.cbor b/test/fixtures/n6/encoding/negative/tag_1_datetime/epoch.cbor new file mode 100644 index 0000000..4ecf946 --- /dev/null +++ b/test/fixtures/n6/encoding/negative/tag_1_datetime/epoch.cbor @@ -0,0 +1 @@ +ÁQKg° \ No newline at end of file diff --git a/test/fixtures/n6/encoding/negative/tag_1_datetime/epoch.expect b/test/fixtures/n6/encoding/negative/tag_1_datetime/epoch.expect new file mode 100644 index 0000000..dc1bd04 --- /dev/null +++ b/test/fixtures/n6/encoding/negative/tag_1_datetime/epoch.expect @@ -0,0 +1 @@ +{"exit":1,"source":"RFC 8949 Appendix A","verdict_code":"tag_forbidden"} diff --git a/test/fixtures/n6/encoding/negative/tag_1_datetime/nested_in_array.cbor b/test/fixtures/n6/encoding/negative/tag_1_datetime/nested_in_array.cbor new file mode 100644 index 0000000..1bc36d6 --- /dev/null +++ b/test/fixtures/n6/encoding/negative/tag_1_datetime/nested_in_array.cbor @@ -0,0 +1 @@ +�ÁQKg° \ No newline at end of file diff --git a/test/fixtures/n6/encoding/negative/tag_1_datetime/nested_in_array.expect b/test/fixtures/n6/encoding/negative/tag_1_datetime/nested_in_array.expect new file mode 100644 index 0000000..87a0185 --- /dev/null +++ b/test/fixtures/n6/encoding/negative/tag_1_datetime/nested_in_array.expect @@ -0,0 +1 @@ +{"exit":1,"source":"hand","verdict_code":"tag_forbidden"} diff --git a/test/fixtures/n6/encoding/negative/tag_bignum_small/positive_bignum_1.cbor b/test/fixtures/n6/encoding/negative/tag_bignum_small/positive_bignum_1.cbor new file mode 100644 index 0000000..057852e --- /dev/null +++ b/test/fixtures/n6/encoding/negative/tag_bignum_small/positive_bignum_1.cbor @@ -0,0 +1 @@ +ÂA \ No newline at end of file diff --git a/test/fixtures/n6/encoding/negative/tag_bignum_small/positive_bignum_1.expect b/test/fixtures/n6/encoding/negative/tag_bignum_small/positive_bignum_1.expect new file mode 100644 index 0000000..87a0185 --- /dev/null +++ b/test/fixtures/n6/encoding/negative/tag_bignum_small/positive_bignum_1.expect @@ -0,0 +1 @@ +{"exit":1,"source":"hand","verdict_code":"tag_forbidden"} diff --git a/test/fixtures/n6/encoding/negative/tag_bignum_small/rfc_2_64.cbor b/test/fixtures/n6/encoding/negative/tag_bignum_small/rfc_2_64.cbor new file mode 100644 index 0000000000000000000000000000000000000000..00a3f0c5362935b7b6818bd076c0a6660a2535d7 GIT binary patch literal 11 McmX@)$;bc&01HF`4FCWD literal 0 HcmV?d00001 diff --git a/test/fixtures/n6/encoding/negative/tag_bignum_small/rfc_2_64.expect b/test/fixtures/n6/encoding/negative/tag_bignum_small/rfc_2_64.expect new file mode 100644 index 0000000..dc1bd04 --- /dev/null +++ b/test/fixtures/n6/encoding/negative/tag_bignum_small/rfc_2_64.expect @@ -0,0 +1 @@ +{"exit":1,"source":"RFC 8949 Appendix A","verdict_code":"tag_forbidden"} diff --git a/test/fixtures/n6/encoding/negative/tag_cose_sign1/tag_18.cbor b/test/fixtures/n6/encoding/negative/tag_cose_sign1/tag_18.cbor new file mode 100644 index 0000000..4081792 --- /dev/null +++ b/test/fixtures/n6/encoding/negative/tag_cose_sign1/tag_18.cbor @@ -0,0 +1 @@ +Ò€ \ No newline at end of file diff --git a/test/fixtures/n6/encoding/negative/tag_cose_sign1/tag_18.expect b/test/fixtures/n6/encoding/negative/tag_cose_sign1/tag_18.expect new file mode 100644 index 0000000..278d067 --- /dev/null +++ b/test/fixtures/n6/encoding/negative/tag_cose_sign1/tag_18.expect @@ -0,0 +1 @@ +{"exit":1,"source":"hand: COSE_Sign1's tag inside N6 bytes","verdict_code":"tag_forbidden"} diff --git a/test/fixtures/n6/encoding/negative/text_bad_utf8/lone_ff.cbor b/test/fixtures/n6/encoding/negative/text_bad_utf8/lone_ff.cbor new file mode 100644 index 0000000..5800d26 --- /dev/null +++ b/test/fixtures/n6/encoding/negative/text_bad_utf8/lone_ff.cbor @@ -0,0 +1 @@ +baÿ \ No newline at end of file diff --git a/test/fixtures/n6/encoding/negative/text_bad_utf8/lone_ff.expect b/test/fixtures/n6/encoding/negative/text_bad_utf8/lone_ff.expect new file mode 100644 index 0000000..6ad6630 --- /dev/null +++ b/test/fixtures/n6/encoding/negative/text_bad_utf8/lone_ff.expect @@ -0,0 +1 @@ +{"exit":1,"source":"hand","verdict_code":"invalid_utf8"} diff --git a/test/fixtures/n6/encoding/negative/text_bad_utf8/truncated_sequence.cbor b/test/fixtures/n6/encoding/negative/text_bad_utf8/truncated_sequence.cbor new file mode 100644 index 0000000..1512cdf --- /dev/null +++ b/test/fixtures/n6/encoding/negative/text_bad_utf8/truncated_sequence.cbor @@ -0,0 +1 @@ +baà \ No newline at end of file diff --git a/test/fixtures/n6/encoding/negative/text_bad_utf8/truncated_sequence.expect b/test/fixtures/n6/encoding/negative/text_bad_utf8/truncated_sequence.expect new file mode 100644 index 0000000..6ad6630 --- /dev/null +++ b/test/fixtures/n6/encoding/negative/text_bad_utf8/truncated_sequence.expect @@ -0,0 +1 @@ +{"exit":1,"source":"hand","verdict_code":"invalid_utf8"} diff --git a/test/fixtures/n6/encoding/negative/text_key/rfc_map.cbor b/test/fixtures/n6/encoding/negative/text_key/rfc_map.cbor new file mode 100644 index 0000000..81e3847 --- /dev/null +++ b/test/fixtures/n6/encoding/negative/text_key/rfc_map.cbor @@ -0,0 +1 @@ +¢aaab‚ \ No newline at end of file diff --git a/test/fixtures/n6/encoding/negative/text_key/rfc_map.expect b/test/fixtures/n6/encoding/negative/text_key/rfc_map.expect new file mode 100644 index 0000000..a8acb52 --- /dev/null +++ b/test/fixtures/n6/encoding/negative/text_key/rfc_map.expect @@ -0,0 +1 @@ +{"exit":1,"source":"RFC 8949 Appendix A","verdict_code":"key_not_uint"} diff --git a/test/fixtures/n6/encoding/negative/text_key/top.cbor b/test/fixtures/n6/encoding/negative/text_key/top.cbor new file mode 100644 index 0000000..faa8a3d --- /dev/null +++ b/test/fixtures/n6/encoding/negative/text_key/top.cbor @@ -0,0 +1 @@ +¡aa \ No newline at end of file diff --git a/test/fixtures/n6/encoding/negative/text_key/top.expect b/test/fixtures/n6/encoding/negative/text_key/top.expect new file mode 100644 index 0000000..bfc8da5 --- /dev/null +++ b/test/fixtures/n6/encoding/negative/text_key/top.expect @@ -0,0 +1 @@ +{"exit":1,"source":"hand","verdict_code":"key_not_uint"} diff --git a/test/fixtures/n6/encoding/negative/text_nfd/e_combining_acute.cbor b/test/fixtures/n6/encoding/negative/text_nfd/e_combining_acute.cbor new file mode 100644 index 0000000..2435822 --- /dev/null +++ b/test/fixtures/n6/encoding/negative/text_nfd/e_combining_acute.cbor @@ -0,0 +1 @@ +ceÌ� \ No newline at end of file diff --git a/test/fixtures/n6/encoding/negative/text_nfd/e_combining_acute.expect b/test/fixtures/n6/encoding/negative/text_nfd/e_combining_acute.expect new file mode 100644 index 0000000..aef4e85 --- /dev/null +++ b/test/fixtures/n6/encoding/negative/text_nfd/e_combining_acute.expect @@ -0,0 +1 @@ +{"exit":1,"source":"hand: U+0065 U+0301","verdict_code":"not_nfc"} diff --git a/test/fixtures/n6/encoding/negative/text_nfd/nested_in_map_value.cbor b/test/fixtures/n6/encoding/negative/text_nfd/nested_in_map_value.cbor new file mode 100644 index 0000000..0021080 --- /dev/null +++ b/test/fixtures/n6/encoding/negative/text_nfd/nested_in_map_value.cbor @@ -0,0 +1 @@ +¡ceÌ� \ No newline at end of file diff --git a/test/fixtures/n6/encoding/negative/text_nfd/nested_in_map_value.expect b/test/fixtures/n6/encoding/negative/text_nfd/nested_in_map_value.expect new file mode 100644 index 0000000..8a28774 --- /dev/null +++ b/test/fixtures/n6/encoding/negative/text_nfd/nested_in_map_value.expect @@ -0,0 +1 @@ +{"exit":1,"source":"hand","verdict_code":"not_nfc"} diff --git a/test/fixtures/n6/encoding/negative/text_overlong_utf8/slash.cbor b/test/fixtures/n6/encoding/negative/text_overlong_utf8/slash.cbor new file mode 100644 index 0000000..619246b --- /dev/null +++ b/test/fixtures/n6/encoding/negative/text_overlong_utf8/slash.cbor @@ -0,0 +1 @@ +bÀ¯ \ No newline at end of file diff --git a/test/fixtures/n6/encoding/negative/text_overlong_utf8/slash.expect b/test/fixtures/n6/encoding/negative/text_overlong_utf8/slash.expect new file mode 100644 index 0000000..9c55340 --- /dev/null +++ b/test/fixtures/n6/encoding/negative/text_overlong_utf8/slash.expect @@ -0,0 +1 @@ +{"exit":1,"source":"hand: '/' in two bytes","verdict_code":"invalid_utf8"} diff --git a/test/fixtures/n6/encoding/negative/text_surrogate/d800.cbor b/test/fixtures/n6/encoding/negative/text_surrogate/d800.cbor new file mode 100644 index 0000000..3b04756 --- /dev/null +++ b/test/fixtures/n6/encoding/negative/text_surrogate/d800.cbor @@ -0,0 +1 @@ +cí € \ No newline at end of file diff --git a/test/fixtures/n6/encoding/negative/text_surrogate/d800.expect b/test/fixtures/n6/encoding/negative/text_surrogate/d800.expect new file mode 100644 index 0000000..7286f7d --- /dev/null +++ b/test/fixtures/n6/encoding/negative/text_surrogate/d800.expect @@ -0,0 +1 @@ +{"exit":1,"source":"hand: U+D800 in UTF-8 form","verdict_code":"invalid_utf8"} diff --git a/test/fixtures/n6/encoding/negative/too_deep/depth_17.cbor b/test/fixtures/n6/encoding/negative/too_deep/depth_17.cbor new file mode 100644 index 0000000..e2b905b --- /dev/null +++ b/test/fixtures/n6/encoding/negative/too_deep/depth_17.cbor @@ -0,0 +1 @@ +����������������€ \ No newline at end of file diff --git a/test/fixtures/n6/encoding/negative/too_deep/depth_17.expect b/test/fixtures/n6/encoding/negative/too_deep/depth_17.expect new file mode 100644 index 0000000..0361d1c --- /dev/null +++ b/test/fixtures/n6/encoding/negative/too_deep/depth_17.expect @@ -0,0 +1 @@ +{"exit":1,"source":"hand: one past the depth limit","verdict_code":"too_deep"} diff --git a/test/fixtures/n6/encoding/negative/trailing_bytes/after_array.cbor b/test/fixtures/n6/encoding/negative/trailing_bytes/after_array.cbor new file mode 100644 index 0000000000000000000000000000000000000000..438e74a03d19519f3369fe66bbb3a87592888623 GIT binary patch literal 2 JcmZo*000330D%Ai literal 0 HcmV?d00001 diff --git a/test/fixtures/n6/encoding/negative/trailing_bytes/after_array.expect b/test/fixtures/n6/encoding/negative/trailing_bytes/after_array.expect new file mode 100644 index 0000000..7e07a88 --- /dev/null +++ b/test/fixtures/n6/encoding/negative/trailing_bytes/after_array.expect @@ -0,0 +1 @@ +{"exit":1,"source":"hand","verdict_code":"trailing_bytes"} diff --git a/test/fixtures/n6/encoding/negative/trailing_bytes/two_items.cbor b/test/fixtures/n6/encoding/negative/trailing_bytes/two_items.cbor new file mode 100644 index 0000000000000000000000000000000000000000..09f370e38f498a462e1ca0faa724559b6630c04f GIT binary patch literal 2 JcmZQz0000200961 literal 0 HcmV?d00001 diff --git a/test/fixtures/n6/encoding/negative/trailing_bytes/two_items.expect b/test/fixtures/n6/encoding/negative/trailing_bytes/two_items.expect new file mode 100644 index 0000000..7e07a88 --- /dev/null +++ b/test/fixtures/n6/encoding/negative/trailing_bytes/two_items.expect @@ -0,0 +1 @@ +{"exit":1,"source":"hand","verdict_code":"trailing_bytes"} diff --git a/test/fixtures/n6/encoding/negative/truncated/array_items.cbor b/test/fixtures/n6/encoding/negative/truncated/array_items.cbor new file mode 100644 index 0000000..e765843 --- /dev/null +++ b/test/fixtures/n6/encoding/negative/truncated/array_items.cbor @@ -0,0 +1 @@ +ƒ \ No newline at end of file diff --git a/test/fixtures/n6/encoding/negative/truncated/array_items.expect b/test/fixtures/n6/encoding/negative/truncated/array_items.expect new file mode 100644 index 0000000..d2e8055 --- /dev/null +++ b/test/fixtures/n6/encoding/negative/truncated/array_items.expect @@ -0,0 +1 @@ +{"exit":1,"source":"hand","verdict_code":"truncated"} diff --git a/test/fixtures/n6/encoding/negative/truncated/bstr_body.cbor b/test/fixtures/n6/encoding/negative/truncated/bstr_body.cbor new file mode 100644 index 0000000..088a63b --- /dev/null +++ b/test/fixtures/n6/encoding/negative/truncated/bstr_body.cbor @@ -0,0 +1 @@ +D \ No newline at end of file diff --git a/test/fixtures/n6/encoding/negative/truncated/bstr_body.expect b/test/fixtures/n6/encoding/negative/truncated/bstr_body.expect new file mode 100644 index 0000000..d2e8055 --- /dev/null +++ b/test/fixtures/n6/encoding/negative/truncated/bstr_body.expect @@ -0,0 +1 @@ +{"exit":1,"source":"hand","verdict_code":"truncated"} diff --git a/test/fixtures/n6/encoding/negative/truncated/empty_input.cbor b/test/fixtures/n6/encoding/negative/truncated/empty_input.cbor new file mode 100644 index 0000000..e69de29 diff --git a/test/fixtures/n6/encoding/negative/truncated/empty_input.expect b/test/fixtures/n6/encoding/negative/truncated/empty_input.expect new file mode 100644 index 0000000..d2e8055 --- /dev/null +++ b/test/fixtures/n6/encoding/negative/truncated/empty_input.expect @@ -0,0 +1 @@ +{"exit":1,"source":"hand","verdict_code":"truncated"} diff --git a/test/fixtures/n6/encoding/negative/truncated/head_argument.cbor b/test/fixtures/n6/encoding/negative/truncated/head_argument.cbor new file mode 100644 index 0000000..48855df --- /dev/null +++ b/test/fixtures/n6/encoding/negative/truncated/head_argument.cbor @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/test/fixtures/n6/encoding/negative/truncated/head_argument.expect b/test/fixtures/n6/encoding/negative/truncated/head_argument.expect new file mode 100644 index 0000000..d2e8055 --- /dev/null +++ b/test/fixtures/n6/encoding/negative/truncated/head_argument.expect @@ -0,0 +1 @@ +{"exit":1,"source":"hand","verdict_code":"truncated"} diff --git a/test/fixtures/n6/encoding/negative/undefined/top.cbor b/test/fixtures/n6/encoding/negative/undefined/top.cbor new file mode 100644 index 0000000..009080e --- /dev/null +++ b/test/fixtures/n6/encoding/negative/undefined/top.cbor @@ -0,0 +1 @@ +÷ \ No newline at end of file diff --git a/test/fixtures/n6/encoding/negative/undefined/top.expect b/test/fixtures/n6/encoding/negative/undefined/top.expect new file mode 100644 index 0000000..174fe69 --- /dev/null +++ b/test/fixtures/n6/encoding/negative/undefined/top.expect @@ -0,0 +1 @@ +{"exit":1,"source":"RFC 8949 Appendix A","verdict_code":"simple_forbidden"} diff --git a/test/fixtures/n6/encoding/positive/array_123.cbor b/test/fixtures/n6/encoding/positive/array_123.cbor new file mode 100644 index 0000000..4cdf6ce --- /dev/null +++ b/test/fixtures/n6/encoding/positive/array_123.cbor @@ -0,0 +1 @@ +ƒ \ No newline at end of file diff --git a/test/fixtures/n6/encoding/positive/array_123.desc.json b/test/fixtures/n6/encoding/positive/array_123.desc.json new file mode 100644 index 0000000..8a86552 --- /dev/null +++ b/test/fixtures/n6/encoding/positive/array_123.desc.json @@ -0,0 +1 @@ +{"source":"RFC 8949 Appendix A","value":{"a":[{"i":"1"},{"i":"2"},{"i":"3"}]}} diff --git a/test/fixtures/n6/encoding/positive/array_123.diag b/test/fixtures/n6/encoding/positive/array_123.diag new file mode 100644 index 0000000..b5d8bb5 --- /dev/null +++ b/test/fixtures/n6/encoding/positive/array_123.diag @@ -0,0 +1 @@ +[1, 2, 3] diff --git a/test/fixtures/n6/encoding/positive/array_25.cbor b/test/fixtures/n6/encoding/positive/array_25.cbor new file mode 100644 index 0000000..b449584 --- /dev/null +++ b/test/fixtures/n6/encoding/positive/array_25.cbor @@ -0,0 +1,2 @@ +˜ +  \ No newline at end of file diff --git a/test/fixtures/n6/encoding/positive/array_25.desc.json b/test/fixtures/n6/encoding/positive/array_25.desc.json new file mode 100644 index 0000000..df8ffb7 --- /dev/null +++ b/test/fixtures/n6/encoding/positive/array_25.desc.json @@ -0,0 +1 @@ +{"source":"RFC 8949 Appendix A","value":{"a":[{"i":"1"},{"i":"2"},{"i":"3"},{"i":"4"},{"i":"5"},{"i":"6"},{"i":"7"},{"i":"8"},{"i":"9"},{"i":"10"},{"i":"11"},{"i":"12"},{"i":"13"},{"i":"14"},{"i":"15"},{"i":"16"},{"i":"17"},{"i":"18"},{"i":"19"},{"i":"20"},{"i":"21"},{"i":"22"},{"i":"23"},{"i":"24"},{"i":"25"}]}} diff --git a/test/fixtures/n6/encoding/positive/array_25.diag b/test/fixtures/n6/encoding/positive/array_25.diag new file mode 100644 index 0000000..21eacd5 --- /dev/null +++ b/test/fixtures/n6/encoding/positive/array_25.diag @@ -0,0 +1 @@ +[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25] diff --git a/test/fixtures/n6/encoding/positive/array_empty.cbor b/test/fixtures/n6/encoding/positive/array_empty.cbor new file mode 100644 index 0000000..5416677 --- /dev/null +++ b/test/fixtures/n6/encoding/positive/array_empty.cbor @@ -0,0 +1 @@ +€ \ No newline at end of file diff --git a/test/fixtures/n6/encoding/positive/array_empty.desc.json b/test/fixtures/n6/encoding/positive/array_empty.desc.json new file mode 100644 index 0000000..b442366 --- /dev/null +++ b/test/fixtures/n6/encoding/positive/array_empty.desc.json @@ -0,0 +1 @@ +{"source":"RFC 8949 Appendix A","value":{"a":[]}} diff --git a/test/fixtures/n6/encoding/positive/array_empty.diag b/test/fixtures/n6/encoding/positive/array_empty.diag new file mode 100644 index 0000000..fe51488 --- /dev/null +++ b/test/fixtures/n6/encoding/positive/array_empty.diag @@ -0,0 +1 @@ +[] diff --git a/test/fixtures/n6/encoding/positive/array_nested.cbor b/test/fixtures/n6/encoding/positive/array_nested.cbor new file mode 100644 index 0000000..80fd394 --- /dev/null +++ b/test/fixtures/n6/encoding/positive/array_nested.cbor @@ -0,0 +1 @@ +ƒ‚‚ \ No newline at end of file diff --git a/test/fixtures/n6/encoding/positive/array_nested.desc.json b/test/fixtures/n6/encoding/positive/array_nested.desc.json new file mode 100644 index 0000000..b4a2df0 --- /dev/null +++ b/test/fixtures/n6/encoding/positive/array_nested.desc.json @@ -0,0 +1 @@ +{"source":"RFC 8949 Appendix A","value":{"a":[{"i":"1"},{"a":[{"i":"2"},{"i":"3"}]},{"a":[{"i":"4"},{"i":"5"}]}]}} diff --git a/test/fixtures/n6/encoding/positive/array_nested.diag b/test/fixtures/n6/encoding/positive/array_nested.diag new file mode 100644 index 0000000..41ad53d --- /dev/null +++ b/test/fixtures/n6/encoding/positive/array_nested.diag @@ -0,0 +1 @@ +[1, [2, 3], [4, 5]] diff --git a/test/fixtures/n6/encoding/positive/bstr_01020304.cbor b/test/fixtures/n6/encoding/positive/bstr_01020304.cbor new file mode 100644 index 0000000..352e89a --- /dev/null +++ b/test/fixtures/n6/encoding/positive/bstr_01020304.cbor @@ -0,0 +1 @@ +D \ No newline at end of file diff --git a/test/fixtures/n6/encoding/positive/bstr_01020304.desc.json b/test/fixtures/n6/encoding/positive/bstr_01020304.desc.json new file mode 100644 index 0000000..bd32819 --- /dev/null +++ b/test/fixtures/n6/encoding/positive/bstr_01020304.desc.json @@ -0,0 +1 @@ +{"source":"RFC 8949 Appendix A","value":{"b":"01020304"}} diff --git a/test/fixtures/n6/encoding/positive/bstr_01020304.diag b/test/fixtures/n6/encoding/positive/bstr_01020304.diag new file mode 100644 index 0000000..fd51f5f --- /dev/null +++ b/test/fixtures/n6/encoding/positive/bstr_01020304.diag @@ -0,0 +1 @@ +h'01020304' diff --git a/test/fixtures/n6/encoding/positive/bstr_empty.cbor b/test/fixtures/n6/encoding/positive/bstr_empty.cbor new file mode 100644 index 0000000..b516b2c --- /dev/null +++ b/test/fixtures/n6/encoding/positive/bstr_empty.cbor @@ -0,0 +1 @@ +@ \ No newline at end of file diff --git a/test/fixtures/n6/encoding/positive/bstr_empty.desc.json b/test/fixtures/n6/encoding/positive/bstr_empty.desc.json new file mode 100644 index 0000000..0699a04 --- /dev/null +++ b/test/fixtures/n6/encoding/positive/bstr_empty.desc.json @@ -0,0 +1 @@ +{"source":"RFC 8949 Appendix A","value":{"b":""}} diff --git a/test/fixtures/n6/encoding/positive/bstr_empty.diag b/test/fixtures/n6/encoding/positive/bstr_empty.diag new file mode 100644 index 0000000..cf7ca68 --- /dev/null +++ b/test/fixtures/n6/encoding/positive/bstr_empty.diag @@ -0,0 +1 @@ +h'' diff --git a/test/fixtures/n6/encoding/positive/depth_16.cbor b/test/fixtures/n6/encoding/positive/depth_16.cbor new file mode 100644 index 0000000..9e978e8 --- /dev/null +++ b/test/fixtures/n6/encoding/positive/depth_16.cbor @@ -0,0 +1 @@ +���������������€ \ No newline at end of file diff --git a/test/fixtures/n6/encoding/positive/depth_16.desc.json b/test/fixtures/n6/encoding/positive/depth_16.desc.json new file mode 100644 index 0000000..bff29c5 --- /dev/null +++ b/test/fixtures/n6/encoding/positive/depth_16.desc.json @@ -0,0 +1 @@ +{"source":"hand: the depth limit, counted as Trinity.N6.Profile states","value":{"a":[{"a":[{"a":[{"a":[{"a":[{"a":[{"a":[{"a":[{"a":[{"a":[{"a":[{"a":[{"a":[{"a":[{"a":[{"a":[]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}} diff --git a/test/fixtures/n6/encoding/positive/depth_16.diag b/test/fixtures/n6/encoding/positive/depth_16.diag new file mode 100644 index 0000000..6f5852e --- /dev/null +++ b/test/fixtures/n6/encoding/positive/depth_16.diag @@ -0,0 +1 @@ +[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]] diff --git a/test/fixtures/n6/encoding/positive/false.cbor b/test/fixtures/n6/encoding/positive/false.cbor new file mode 100644 index 0000000..3a6e607 --- /dev/null +++ b/test/fixtures/n6/encoding/positive/false.cbor @@ -0,0 +1 @@ +ô \ No newline at end of file diff --git a/test/fixtures/n6/encoding/positive/false.desc.json b/test/fixtures/n6/encoding/positive/false.desc.json new file mode 100644 index 0000000..03aac6f --- /dev/null +++ b/test/fixtures/n6/encoding/positive/false.desc.json @@ -0,0 +1 @@ +{"source":"RFC 8949 Appendix A","value":{"s":"false"}} diff --git a/test/fixtures/n6/encoding/positive/false.diag b/test/fixtures/n6/encoding/positive/false.diag new file mode 100644 index 0000000..c508d53 --- /dev/null +++ b/test/fixtures/n6/encoding/positive/false.diag @@ -0,0 +1 @@ +false diff --git a/test/fixtures/n6/encoding/positive/map_1_2_3_4.cbor b/test/fixtures/n6/encoding/positive/map_1_2_3_4.cbor new file mode 100644 index 0000000..abc169a --- /dev/null +++ b/test/fixtures/n6/encoding/positive/map_1_2_3_4.cbor @@ -0,0 +1 @@ +¢ \ No newline at end of file diff --git a/test/fixtures/n6/encoding/positive/map_1_2_3_4.desc.json b/test/fixtures/n6/encoding/positive/map_1_2_3_4.desc.json new file mode 100644 index 0000000..31abceb --- /dev/null +++ b/test/fixtures/n6/encoding/positive/map_1_2_3_4.desc.json @@ -0,0 +1 @@ +{"source":"RFC 8949 Appendix A","value":{"m":[[{"i":"1"},{"i":"2"}],[{"i":"3"},{"i":"4"}]]}} diff --git a/test/fixtures/n6/encoding/positive/map_1_2_3_4.diag b/test/fixtures/n6/encoding/positive/map_1_2_3_4.diag new file mode 100644 index 0000000..b582a5f --- /dev/null +++ b/test/fixtures/n6/encoding/positive/map_1_2_3_4.diag @@ -0,0 +1 @@ +{1: 2, 3: 4} diff --git a/test/fixtures/n6/encoding/positive/map_empty.cbor b/test/fixtures/n6/encoding/positive/map_empty.cbor new file mode 100644 index 0000000..eea1bf0 --- /dev/null +++ b/test/fixtures/n6/encoding/positive/map_empty.cbor @@ -0,0 +1 @@ +  \ No newline at end of file diff --git a/test/fixtures/n6/encoding/positive/map_empty.desc.json b/test/fixtures/n6/encoding/positive/map_empty.desc.json new file mode 100644 index 0000000..06b7612 --- /dev/null +++ b/test/fixtures/n6/encoding/positive/map_empty.desc.json @@ -0,0 +1 @@ +{"source":"RFC 8949 Appendix A","value":{"m":[]}} diff --git a/test/fixtures/n6/encoding/positive/map_empty.diag b/test/fixtures/n6/encoding/positive/map_empty.diag new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/test/fixtures/n6/encoding/positive/map_empty.diag @@ -0,0 +1 @@ +{} diff --git a/test/fixtures/n6/encoding/positive/map_key_max_u32.cbor b/test/fixtures/n6/encoding/positive/map_key_max_u32.cbor new file mode 100644 index 0000000000000000000000000000000000000000..4db40e10387f1d241d4c93299b8b7f899e05ba81 GIT binary patch literal 7 McmZ1|^&bcr022HJxBvhE literal 0 HcmV?d00001 diff --git a/test/fixtures/n6/encoding/positive/map_key_max_u32.desc.json b/test/fixtures/n6/encoding/positive/map_key_max_u32.desc.json new file mode 100644 index 0000000..e1e6a4e --- /dev/null +++ b/test/fixtures/n6/encoding/positive/map_key_max_u32.desc.json @@ -0,0 +1 @@ +{"source":"hand: the top of E2","value":{"m":[[{"i":"4294967295"},{"i":"0"}]]}} diff --git a/test/fixtures/n6/encoding/positive/map_key_max_u32.diag b/test/fixtures/n6/encoding/positive/map_key_max_u32.diag new file mode 100644 index 0000000..02205ca --- /dev/null +++ b/test/fixtures/n6/encoding/positive/map_key_max_u32.diag @@ -0,0 +1 @@ +{4294967295: 0} diff --git a/test/fixtures/n6/encoding/positive/map_keys_across_heads.cbor b/test/fixtures/n6/encoding/positive/map_keys_across_heads.cbor new file mode 100644 index 0000000000000000000000000000000000000000..8c925e5828df91c9fdc6203582c1e1e2c3a2df29 GIT binary patch literal 15 WcmZ3=#URcoA;Bb(!Ys-Bf&~B)jRJuH literal 0 HcmV?d00001 diff --git a/test/fixtures/n6/encoding/positive/map_keys_across_heads.desc.json b/test/fixtures/n6/encoding/positive/map_keys_across_heads.desc.json new file mode 100644 index 0000000..88390e5 --- /dev/null +++ b/test/fixtures/n6/encoding/positive/map_keys_across_heads.desc.json @@ -0,0 +1 @@ +{"source":"hand: unsigned keys with 0-, 1- and 2-byte arguments, in bytewise order","value":{"m":[[{"i":"10"},{"i":"0"}],[{"i":"23"},{"i":"1"}],[{"i":"24"},{"i":"2"}],[{"i":"100"},{"i":"3"}],[{"i":"1000"},{"i":"4"}]]}} diff --git a/test/fixtures/n6/encoding/positive/map_keys_across_heads.diag b/test/fixtures/n6/encoding/positive/map_keys_across_heads.diag new file mode 100644 index 0000000..66aa623 --- /dev/null +++ b/test/fixtures/n6/encoding/positive/map_keys_across_heads.diag @@ -0,0 +1 @@ +{10: 0, 23: 1, 24: 2, 100: 3, 1000: 4} diff --git a/test/fixtures/n6/encoding/positive/map_mixed_values.cbor b/test/fixtures/n6/encoding/positive/map_mixed_values.cbor new file mode 100644 index 0000000000000000000000000000000000000000..dfab089ba42beebc3536ca537275b8e98c6dd111 GIT binary patch literal 13 UcmZ3&z{u$MpDD3|x#{aS02|c>G5`Po literal 0 HcmV?d00001 diff --git a/test/fixtures/n6/encoding/positive/map_mixed_values.desc.json b/test/fixtures/n6/encoding/positive/map_mixed_values.desc.json new file mode 100644 index 0000000..58d8e73 --- /dev/null +++ b/test/fixtures/n6/encoding/positive/map_mixed_values.desc.json @@ -0,0 +1 @@ +{"source":"hand","value":{"m":[[{"i":"0"},{"i":"1"}],[{"i":"1"},{"b":"ff"}],[{"i":"2"},{"t":"78"}],[{"i":"3"},{"a":[{"s":"true"},{"s":"null"}]}]]}} diff --git a/test/fixtures/n6/encoding/positive/map_mixed_values.diag b/test/fixtures/n6/encoding/positive/map_mixed_values.diag new file mode 100644 index 0000000..52d52ae --- /dev/null +++ b/test/fixtures/n6/encoding/positive/map_mixed_values.diag @@ -0,0 +1 @@ +{0: 1, 1: h'ff', 2: "x", 3: [true, null]} diff --git a/test/fixtures/n6/encoding/positive/nint_1.cbor b/test/fixtures/n6/encoding/positive/nint_1.cbor new file mode 100644 index 0000000..0519ecb --- /dev/null +++ b/test/fixtures/n6/encoding/positive/nint_1.cbor @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/test/fixtures/n6/encoding/positive/nint_1.desc.json b/test/fixtures/n6/encoding/positive/nint_1.desc.json new file mode 100644 index 0000000..3291d6c --- /dev/null +++ b/test/fixtures/n6/encoding/positive/nint_1.desc.json @@ -0,0 +1 @@ +{"source":"RFC 8949 Appendix A","value":{"i":"-1"}} diff --git a/test/fixtures/n6/encoding/positive/nint_1.diag b/test/fixtures/n6/encoding/positive/nint_1.diag new file mode 100644 index 0000000..3a2e3f4 --- /dev/null +++ b/test/fixtures/n6/encoding/positive/nint_1.diag @@ -0,0 +1 @@ +-1 diff --git a/test/fixtures/n6/encoding/positive/nint_10.cbor b/test/fixtures/n6/encoding/positive/nint_10.cbor new file mode 100644 index 0000000..e8a0f87 --- /dev/null +++ b/test/fixtures/n6/encoding/positive/nint_10.cbor @@ -0,0 +1 @@ +) \ No newline at end of file diff --git a/test/fixtures/n6/encoding/positive/nint_10.desc.json b/test/fixtures/n6/encoding/positive/nint_10.desc.json new file mode 100644 index 0000000..e38bd60 --- /dev/null +++ b/test/fixtures/n6/encoding/positive/nint_10.desc.json @@ -0,0 +1 @@ +{"source":"RFC 8949 Appendix A","value":{"i":"-10"}} diff --git a/test/fixtures/n6/encoding/positive/nint_10.diag b/test/fixtures/n6/encoding/positive/nint_10.diag new file mode 100644 index 0000000..2916703 --- /dev/null +++ b/test/fixtures/n6/encoding/positive/nint_10.diag @@ -0,0 +1 @@ +-10 diff --git a/test/fixtures/n6/encoding/positive/nint_100.cbor b/test/fixtures/n6/encoding/positive/nint_100.cbor new file mode 100644 index 0000000..bfefdad --- /dev/null +++ b/test/fixtures/n6/encoding/positive/nint_100.cbor @@ -0,0 +1 @@ +8c \ No newline at end of file diff --git a/test/fixtures/n6/encoding/positive/nint_100.desc.json b/test/fixtures/n6/encoding/positive/nint_100.desc.json new file mode 100644 index 0000000..f221efb --- /dev/null +++ b/test/fixtures/n6/encoding/positive/nint_100.desc.json @@ -0,0 +1 @@ +{"source":"RFC 8949 Appendix A","value":{"i":"-100"}} diff --git a/test/fixtures/n6/encoding/positive/nint_100.diag b/test/fixtures/n6/encoding/positive/nint_100.diag new file mode 100644 index 0000000..e96acb9 --- /dev/null +++ b/test/fixtures/n6/encoding/positive/nint_100.diag @@ -0,0 +1 @@ +-100 diff --git a/test/fixtures/n6/encoding/positive/nint_1000.cbor b/test/fixtures/n6/encoding/positive/nint_1000.cbor new file mode 100644 index 0000000..5de2555 --- /dev/null +++ b/test/fixtures/n6/encoding/positive/nint_1000.cbor @@ -0,0 +1 @@ +9ç \ No newline at end of file diff --git a/test/fixtures/n6/encoding/positive/nint_1000.desc.json b/test/fixtures/n6/encoding/positive/nint_1000.desc.json new file mode 100644 index 0000000..df83dbd --- /dev/null +++ b/test/fixtures/n6/encoding/positive/nint_1000.desc.json @@ -0,0 +1 @@ +{"source":"RFC 8949 Appendix A","value":{"i":"-1000"}} diff --git a/test/fixtures/n6/encoding/positive/nint_1000.diag b/test/fixtures/n6/encoding/positive/nint_1000.diag new file mode 100644 index 0000000..58fe91b --- /dev/null +++ b/test/fixtures/n6/encoding/positive/nint_1000.diag @@ -0,0 +1 @@ +-1000 diff --git a/test/fixtures/n6/encoding/positive/nint_min_i64.cbor b/test/fixtures/n6/encoding/positive/nint_min_i64.cbor new file mode 100644 index 0000000..62bd968 --- /dev/null +++ b/test/fixtures/n6/encoding/positive/nint_min_i64.cbor @@ -0,0 +1 @@ +;ÿÿÿÿÿÿÿ \ No newline at end of file diff --git a/test/fixtures/n6/encoding/positive/nint_min_i64.desc.json b/test/fixtures/n6/encoding/positive/nint_min_i64.desc.json new file mode 100644 index 0000000..300962c --- /dev/null +++ b/test/fixtures/n6/encoding/positive/nint_min_i64.desc.json @@ -0,0 +1 @@ +{"source":"hand: -2^63, the bottom of E4","value":{"i":"-9223372036854775808"}} diff --git a/test/fixtures/n6/encoding/positive/nint_min_i64.diag b/test/fixtures/n6/encoding/positive/nint_min_i64.diag new file mode 100644 index 0000000..7928ab8 --- /dev/null +++ b/test/fixtures/n6/encoding/positive/nint_min_i64.diag @@ -0,0 +1 @@ +-9223372036854775808 diff --git a/test/fixtures/n6/encoding/positive/null.cbor b/test/fixtures/n6/encoding/positive/null.cbor new file mode 100644 index 0000000..f7a8cad --- /dev/null +++ b/test/fixtures/n6/encoding/positive/null.cbor @@ -0,0 +1 @@ +ö \ No newline at end of file diff --git a/test/fixtures/n6/encoding/positive/null.desc.json b/test/fixtures/n6/encoding/positive/null.desc.json new file mode 100644 index 0000000..7133f6e --- /dev/null +++ b/test/fixtures/n6/encoding/positive/null.desc.json @@ -0,0 +1 @@ +{"source":"RFC 8949 Appendix A","value":{"s":"null"}} diff --git a/test/fixtures/n6/encoding/positive/null.diag b/test/fixtures/n6/encoding/positive/null.diag new file mode 100644 index 0000000..19765bd --- /dev/null +++ b/test/fixtures/n6/encoding/positive/null.diag @@ -0,0 +1 @@ +null diff --git a/test/fixtures/n6/encoding/positive/true.cbor b/test/fixtures/n6/encoding/positive/true.cbor new file mode 100644 index 0000000..bb7d13c --- /dev/null +++ b/test/fixtures/n6/encoding/positive/true.cbor @@ -0,0 +1 @@ +õ \ No newline at end of file diff --git a/test/fixtures/n6/encoding/positive/true.desc.json b/test/fixtures/n6/encoding/positive/true.desc.json new file mode 100644 index 0000000..d0867fb --- /dev/null +++ b/test/fixtures/n6/encoding/positive/true.desc.json @@ -0,0 +1 @@ +{"source":"RFC 8949 Appendix A","value":{"s":"true"}} diff --git a/test/fixtures/n6/encoding/positive/true.diag b/test/fixtures/n6/encoding/positive/true.diag new file mode 100644 index 0000000..27ba77d --- /dev/null +++ b/test/fixtures/n6/encoding/positive/true.diag @@ -0,0 +1 @@ +true diff --git a/test/fixtures/n6/encoding/positive/tstr_a.cbor b/test/fixtures/n6/encoding/positive/tstr_a.cbor new file mode 100644 index 0000000..7ec9a4b --- /dev/null +++ b/test/fixtures/n6/encoding/positive/tstr_a.cbor @@ -0,0 +1 @@ +aa \ No newline at end of file diff --git a/test/fixtures/n6/encoding/positive/tstr_a.desc.json b/test/fixtures/n6/encoding/positive/tstr_a.desc.json new file mode 100644 index 0000000..6930fe2 --- /dev/null +++ b/test/fixtures/n6/encoding/positive/tstr_a.desc.json @@ -0,0 +1 @@ +{"source":"RFC 8949 Appendix A","value":{"t":"61"}} diff --git a/test/fixtures/n6/encoding/positive/tstr_a.diag b/test/fixtures/n6/encoding/positive/tstr_a.diag new file mode 100644 index 0000000..231f150 --- /dev/null +++ b/test/fixtures/n6/encoding/positive/tstr_a.diag @@ -0,0 +1 @@ +"a" diff --git a/test/fixtures/n6/encoding/positive/tstr_aegean_10151.cbor b/test/fixtures/n6/encoding/positive/tstr_aegean_10151.cbor new file mode 100644 index 0000000..8ea48a0 --- /dev/null +++ b/test/fixtures/n6/encoding/positive/tstr_aegean_10151.cbor @@ -0,0 +1 @@ +dð�…‘ \ No newline at end of file diff --git a/test/fixtures/n6/encoding/positive/tstr_aegean_10151.desc.json b/test/fixtures/n6/encoding/positive/tstr_aegean_10151.desc.json new file mode 100644 index 0000000..59f4b6b --- /dev/null +++ b/test/fixtures/n6/encoding/positive/tstr_aegean_10151.desc.json @@ -0,0 +1 @@ +{"source":"RFC 8949 Appendix A","value":{"t":"f0908591"}} diff --git a/test/fixtures/n6/encoding/positive/tstr_aegean_10151.diag b/test/fixtures/n6/encoding/positive/tstr_aegean_10151.diag new file mode 100644 index 0000000..364323e --- /dev/null +++ b/test/fixtures/n6/encoding/positive/tstr_aegean_10151.diag @@ -0,0 +1 @@ +"ð�…‘" diff --git a/test/fixtures/n6/encoding/positive/tstr_empty.cbor b/test/fixtures/n6/encoding/positive/tstr_empty.cbor new file mode 100644 index 0000000..64845fb --- /dev/null +++ b/test/fixtures/n6/encoding/positive/tstr_empty.cbor @@ -0,0 +1 @@ +` \ No newline at end of file diff --git a/test/fixtures/n6/encoding/positive/tstr_empty.desc.json b/test/fixtures/n6/encoding/positive/tstr_empty.desc.json new file mode 100644 index 0000000..10a9d03 --- /dev/null +++ b/test/fixtures/n6/encoding/positive/tstr_empty.desc.json @@ -0,0 +1 @@ +{"source":"RFC 8949 Appendix A","value":{"t":""}} diff --git a/test/fixtures/n6/encoding/positive/tstr_empty.diag b/test/fixtures/n6/encoding/positive/tstr_empty.diag new file mode 100644 index 0000000..e16c76d --- /dev/null +++ b/test/fixtures/n6/encoding/positive/tstr_empty.diag @@ -0,0 +1 @@ +"" diff --git a/test/fixtures/n6/encoding/positive/tstr_ietf.cbor b/test/fixtures/n6/encoding/positive/tstr_ietf.cbor new file mode 100644 index 0000000..743669b --- /dev/null +++ b/test/fixtures/n6/encoding/positive/tstr_ietf.cbor @@ -0,0 +1 @@ +dIETF \ No newline at end of file diff --git a/test/fixtures/n6/encoding/positive/tstr_ietf.desc.json b/test/fixtures/n6/encoding/positive/tstr_ietf.desc.json new file mode 100644 index 0000000..9ecb06f --- /dev/null +++ b/test/fixtures/n6/encoding/positive/tstr_ietf.desc.json @@ -0,0 +1 @@ +{"source":"RFC 8949 Appendix A","value":{"t":"49455446"}} diff --git a/test/fixtures/n6/encoding/positive/tstr_ietf.diag b/test/fixtures/n6/encoding/positive/tstr_ietf.diag new file mode 100644 index 0000000..2f3f140 --- /dev/null +++ b/test/fixtures/n6/encoding/positive/tstr_ietf.diag @@ -0,0 +1 @@ +"IETF" diff --git a/test/fixtures/n6/encoding/positive/tstr_quote_backslash.cbor b/test/fixtures/n6/encoding/positive/tstr_quote_backslash.cbor new file mode 100644 index 0000000..cce6615 --- /dev/null +++ b/test/fixtures/n6/encoding/positive/tstr_quote_backslash.cbor @@ -0,0 +1 @@ +b"\ \ No newline at end of file diff --git a/test/fixtures/n6/encoding/positive/tstr_quote_backslash.desc.json b/test/fixtures/n6/encoding/positive/tstr_quote_backslash.desc.json new file mode 100644 index 0000000..ae09deb --- /dev/null +++ b/test/fixtures/n6/encoding/positive/tstr_quote_backslash.desc.json @@ -0,0 +1 @@ +{"source":"RFC 8949 Appendix A","value":{"t":"225c"}} diff --git a/test/fixtures/n6/encoding/positive/tstr_quote_backslash.diag b/test/fixtures/n6/encoding/positive/tstr_quote_backslash.diag new file mode 100644 index 0000000..d66dda7 --- /dev/null +++ b/test/fixtures/n6/encoding/positive/tstr_quote_backslash.diag @@ -0,0 +1 @@ +"\"\\" diff --git a/test/fixtures/n6/encoding/positive/tstr_u_umlaut.cbor b/test/fixtures/n6/encoding/positive/tstr_u_umlaut.cbor new file mode 100644 index 0000000..9055a92 --- /dev/null +++ b/test/fixtures/n6/encoding/positive/tstr_u_umlaut.cbor @@ -0,0 +1 @@ +bü \ No newline at end of file diff --git a/test/fixtures/n6/encoding/positive/tstr_u_umlaut.desc.json b/test/fixtures/n6/encoding/positive/tstr_u_umlaut.desc.json new file mode 100644 index 0000000..2ac4578 --- /dev/null +++ b/test/fixtures/n6/encoding/positive/tstr_u_umlaut.desc.json @@ -0,0 +1 @@ +{"source":"RFC 8949 Appendix A","value":{"t":"c3bc"}} diff --git a/test/fixtures/n6/encoding/positive/tstr_u_umlaut.diag b/test/fixtures/n6/encoding/positive/tstr_u_umlaut.diag new file mode 100644 index 0000000..70a7c0e --- /dev/null +++ b/test/fixtures/n6/encoding/positive/tstr_u_umlaut.diag @@ -0,0 +1 @@ +"ü" diff --git a/test/fixtures/n6/encoding/positive/tstr_water.cbor b/test/fixtures/n6/encoding/positive/tstr_water.cbor new file mode 100644 index 0000000..0868c9a --- /dev/null +++ b/test/fixtures/n6/encoding/positive/tstr_water.cbor @@ -0,0 +1 @@ +cæ°´ \ No newline at end of file diff --git a/test/fixtures/n6/encoding/positive/tstr_water.desc.json b/test/fixtures/n6/encoding/positive/tstr_water.desc.json new file mode 100644 index 0000000..da5bf2f --- /dev/null +++ b/test/fixtures/n6/encoding/positive/tstr_water.desc.json @@ -0,0 +1 @@ +{"source":"RFC 8949 Appendix A","value":{"t":"e6b0b4"}} diff --git a/test/fixtures/n6/encoding/positive/tstr_water.diag b/test/fixtures/n6/encoding/positive/tstr_water.diag new file mode 100644 index 0000000..dfbcb05 --- /dev/null +++ b/test/fixtures/n6/encoding/positive/tstr_water.diag @@ -0,0 +1 @@ +"æ°´" diff --git a/test/fixtures/n6/encoding/positive/uint_0.cbor b/test/fixtures/n6/encoding/positive/uint_0.cbor new file mode 100644 index 0000000000000000000000000000000000000000..f76dd238ade08917e6712764a16a22005a50573d GIT binary patch literal 1 IcmZPo000310RR91 literal 0 HcmV?d00001 diff --git a/test/fixtures/n6/encoding/positive/uint_0.desc.json b/test/fixtures/n6/encoding/positive/uint_0.desc.json new file mode 100644 index 0000000..27ab3a0 --- /dev/null +++ b/test/fixtures/n6/encoding/positive/uint_0.desc.json @@ -0,0 +1 @@ +{"source":"RFC 8949 Appendix A","value":{"i":"0"}} diff --git a/test/fixtures/n6/encoding/positive/uint_0.diag b/test/fixtures/n6/encoding/positive/uint_0.diag new file mode 100644 index 0000000..573541a --- /dev/null +++ b/test/fixtures/n6/encoding/positive/uint_0.diag @@ -0,0 +1 @@ +0 diff --git a/test/fixtures/n6/encoding/positive/uint_1.cbor b/test/fixtures/n6/encoding/positive/uint_1.cbor new file mode 100644 index 0000000..6b2aaa7 --- /dev/null +++ b/test/fixtures/n6/encoding/positive/uint_1.cbor @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/test/fixtures/n6/encoding/positive/uint_1.desc.json b/test/fixtures/n6/encoding/positive/uint_1.desc.json new file mode 100644 index 0000000..0494c01 --- /dev/null +++ b/test/fixtures/n6/encoding/positive/uint_1.desc.json @@ -0,0 +1 @@ +{"source":"RFC 8949 Appendix A","value":{"i":"1"}} diff --git a/test/fixtures/n6/encoding/positive/uint_1.diag b/test/fixtures/n6/encoding/positive/uint_1.diag new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/test/fixtures/n6/encoding/positive/uint_1.diag @@ -0,0 +1 @@ +1 diff --git a/test/fixtures/n6/encoding/positive/uint_10.cbor b/test/fixtures/n6/encoding/positive/uint_10.cbor new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/test/fixtures/n6/encoding/positive/uint_10.cbor @@ -0,0 +1 @@ + diff --git a/test/fixtures/n6/encoding/positive/uint_10.desc.json b/test/fixtures/n6/encoding/positive/uint_10.desc.json new file mode 100644 index 0000000..5314828 --- /dev/null +++ b/test/fixtures/n6/encoding/positive/uint_10.desc.json @@ -0,0 +1 @@ +{"source":"RFC 8949 Appendix A","value":{"i":"10"}} diff --git a/test/fixtures/n6/encoding/positive/uint_10.diag b/test/fixtures/n6/encoding/positive/uint_10.diag new file mode 100644 index 0000000..f599e28 --- /dev/null +++ b/test/fixtures/n6/encoding/positive/uint_10.diag @@ -0,0 +1 @@ +10 diff --git a/test/fixtures/n6/encoding/positive/uint_100.cbor b/test/fixtures/n6/encoding/positive/uint_100.cbor new file mode 100644 index 0000000..3b5c907 --- /dev/null +++ b/test/fixtures/n6/encoding/positive/uint_100.cbor @@ -0,0 +1 @@ +d \ No newline at end of file diff --git a/test/fixtures/n6/encoding/positive/uint_100.desc.json b/test/fixtures/n6/encoding/positive/uint_100.desc.json new file mode 100644 index 0000000..eee7a64 --- /dev/null +++ b/test/fixtures/n6/encoding/positive/uint_100.desc.json @@ -0,0 +1 @@ +{"source":"RFC 8949 Appendix A","value":{"i":"100"}} diff --git a/test/fixtures/n6/encoding/positive/uint_100.diag b/test/fixtures/n6/encoding/positive/uint_100.diag new file mode 100644 index 0000000..29d6383 --- /dev/null +++ b/test/fixtures/n6/encoding/positive/uint_100.diag @@ -0,0 +1 @@ +100 diff --git a/test/fixtures/n6/encoding/positive/uint_1000.cbor b/test/fixtures/n6/encoding/positive/uint_1000.cbor new file mode 100644 index 0000000..fda74fd --- /dev/null +++ b/test/fixtures/n6/encoding/positive/uint_1000.cbor @@ -0,0 +1 @@ +è \ No newline at end of file diff --git a/test/fixtures/n6/encoding/positive/uint_1000.desc.json b/test/fixtures/n6/encoding/positive/uint_1000.desc.json new file mode 100644 index 0000000..7d2e6f8 --- /dev/null +++ b/test/fixtures/n6/encoding/positive/uint_1000.desc.json @@ -0,0 +1 @@ +{"source":"RFC 8949 Appendix A","value":{"i":"1000"}} diff --git a/test/fixtures/n6/encoding/positive/uint_1000.diag b/test/fixtures/n6/encoding/positive/uint_1000.diag new file mode 100644 index 0000000..83b33d2 --- /dev/null +++ b/test/fixtures/n6/encoding/positive/uint_1000.diag @@ -0,0 +1 @@ +1000 diff --git a/test/fixtures/n6/encoding/positive/uint_1000000.cbor b/test/fixtures/n6/encoding/positive/uint_1000000.cbor new file mode 100644 index 0000000000000000000000000000000000000000..916281951cd165d0c94a823aa0180c47935486ac GIT binary patch literal 5 Mcmb1Q;CFHW00DRatN;K2 literal 0 HcmV?d00001 diff --git a/test/fixtures/n6/encoding/positive/uint_1000000.desc.json b/test/fixtures/n6/encoding/positive/uint_1000000.desc.json new file mode 100644 index 0000000..24ed42a --- /dev/null +++ b/test/fixtures/n6/encoding/positive/uint_1000000.desc.json @@ -0,0 +1 @@ +{"source":"RFC 8949 Appendix A","value":{"i":"1000000"}} diff --git a/test/fixtures/n6/encoding/positive/uint_1000000.diag b/test/fixtures/n6/encoding/positive/uint_1000000.diag new file mode 100644 index 0000000..749fce6 --- /dev/null +++ b/test/fixtures/n6/encoding/positive/uint_1000000.diag @@ -0,0 +1 @@ +1000000 diff --git a/test/fixtures/n6/encoding/positive/uint_1000000000000.cbor b/test/fixtures/n6/encoding/positive/uint_1000000000000.cbor new file mode 100644 index 0000000000000000000000000000000000000000..4cdcd7ccc3f0a4bff373449b313d443379473a32 GIT binary patch literal 9 Qcmb1UU|@K0WvKuI01D#*jQ{`u literal 0 HcmV?d00001 diff --git a/test/fixtures/n6/encoding/positive/uint_1000000000000.desc.json b/test/fixtures/n6/encoding/positive/uint_1000000000000.desc.json new file mode 100644 index 0000000..0d63b9e --- /dev/null +++ b/test/fixtures/n6/encoding/positive/uint_1000000000000.desc.json @@ -0,0 +1 @@ +{"source":"RFC 8949 Appendix A","value":{"i":"1000000000000"}} diff --git a/test/fixtures/n6/encoding/positive/uint_1000000000000.diag b/test/fixtures/n6/encoding/positive/uint_1000000000000.diag new file mode 100644 index 0000000..d6ee901 --- /dev/null +++ b/test/fixtures/n6/encoding/positive/uint_1000000000000.diag @@ -0,0 +1 @@ +1000000000000 diff --git a/test/fixtures/n6/encoding/positive/uint_23.cbor b/test/fixtures/n6/encoding/positive/uint_23.cbor new file mode 100644 index 0000000..c96ab3c --- /dev/null +++ b/test/fixtures/n6/encoding/positive/uint_23.cbor @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/test/fixtures/n6/encoding/positive/uint_23.desc.json b/test/fixtures/n6/encoding/positive/uint_23.desc.json new file mode 100644 index 0000000..370e3d2 --- /dev/null +++ b/test/fixtures/n6/encoding/positive/uint_23.desc.json @@ -0,0 +1 @@ +{"source":"RFC 8949 Appendix A","value":{"i":"23"}} diff --git a/test/fixtures/n6/encoding/positive/uint_23.diag b/test/fixtures/n6/encoding/positive/uint_23.diag new file mode 100644 index 0000000..4099407 --- /dev/null +++ b/test/fixtures/n6/encoding/positive/uint_23.diag @@ -0,0 +1 @@ +23 diff --git a/test/fixtures/n6/encoding/positive/uint_24.cbor b/test/fixtures/n6/encoding/positive/uint_24.cbor new file mode 100644 index 0000000..a1910b3 --- /dev/null +++ b/test/fixtures/n6/encoding/positive/uint_24.cbor @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/test/fixtures/n6/encoding/positive/uint_24.desc.json b/test/fixtures/n6/encoding/positive/uint_24.desc.json new file mode 100644 index 0000000..c54e77f --- /dev/null +++ b/test/fixtures/n6/encoding/positive/uint_24.desc.json @@ -0,0 +1 @@ +{"source":"RFC 8949 Appendix A","value":{"i":"24"}} diff --git a/test/fixtures/n6/encoding/positive/uint_24.diag b/test/fixtures/n6/encoding/positive/uint_24.diag new file mode 100644 index 0000000..a45fd52 --- /dev/null +++ b/test/fixtures/n6/encoding/positive/uint_24.diag @@ -0,0 +1 @@ +24 diff --git a/test/fixtures/n6/encoding/positive/uint_25.cbor b/test/fixtures/n6/encoding/positive/uint_25.cbor new file mode 100644 index 0000000..d4e634a --- /dev/null +++ b/test/fixtures/n6/encoding/positive/uint_25.cbor @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/test/fixtures/n6/encoding/positive/uint_25.desc.json b/test/fixtures/n6/encoding/positive/uint_25.desc.json new file mode 100644 index 0000000..2d526aa --- /dev/null +++ b/test/fixtures/n6/encoding/positive/uint_25.desc.json @@ -0,0 +1 @@ +{"source":"RFC 8949 Appendix A","value":{"i":"25"}} diff --git a/test/fixtures/n6/encoding/positive/uint_25.diag b/test/fixtures/n6/encoding/positive/uint_25.diag new file mode 100644 index 0000000..7273c0f --- /dev/null +++ b/test/fixtures/n6/encoding/positive/uint_25.diag @@ -0,0 +1 @@ +25 diff --git a/test/fixtures/n6/encoding/positive/uint_255.cbor b/test/fixtures/n6/encoding/positive/uint_255.cbor new file mode 100644 index 0000000..fde0e98 --- /dev/null +++ b/test/fixtures/n6/encoding/positive/uint_255.cbor @@ -0,0 +1 @@ +ÿ \ No newline at end of file diff --git a/test/fixtures/n6/encoding/positive/uint_255.desc.json b/test/fixtures/n6/encoding/positive/uint_255.desc.json new file mode 100644 index 0000000..ed9402e --- /dev/null +++ b/test/fixtures/n6/encoding/positive/uint_255.desc.json @@ -0,0 +1 @@ +{"source":"hand: the top of a one-byte argument","value":{"i":"255"}} diff --git a/test/fixtures/n6/encoding/positive/uint_255.diag b/test/fixtures/n6/encoding/positive/uint_255.diag new file mode 100644 index 0000000..ace9d03 --- /dev/null +++ b/test/fixtures/n6/encoding/positive/uint_255.diag @@ -0,0 +1 @@ +255 diff --git a/test/fixtures/n6/encoding/positive/uint_256.cbor b/test/fixtures/n6/encoding/positive/uint_256.cbor new file mode 100644 index 0000000000000000000000000000000000000000..55e7ad0069d067212032cc55de8b27b595bcece2 GIT binary patch literal 3 Kcmb1SWB>pFPyibM literal 0 HcmV?d00001 diff --git a/test/fixtures/n6/encoding/positive/uint_256.desc.json b/test/fixtures/n6/encoding/positive/uint_256.desc.json new file mode 100644 index 0000000..6b2bd63 --- /dev/null +++ b/test/fixtures/n6/encoding/positive/uint_256.desc.json @@ -0,0 +1 @@ +{"source":"hand: the bottom of a two-byte argument","value":{"i":"256"}} diff --git a/test/fixtures/n6/encoding/positive/uint_256.diag b/test/fixtures/n6/encoding/positive/uint_256.diag new file mode 100644 index 0000000..9183bf0 --- /dev/null +++ b/test/fixtures/n6/encoding/positive/uint_256.diag @@ -0,0 +1 @@ +256 diff --git a/test/fixtures/n6/encoding/positive/uint_4294967295.cbor b/test/fixtures/n6/encoding/positive/uint_4294967295.cbor new file mode 100644 index 0000000..153c1c8 --- /dev/null +++ b/test/fixtures/n6/encoding/positive/uint_4294967295.cbor @@ -0,0 +1 @@ +ÿÿÿÿ \ No newline at end of file diff --git a/test/fixtures/n6/encoding/positive/uint_4294967295.desc.json b/test/fixtures/n6/encoding/positive/uint_4294967295.desc.json new file mode 100644 index 0000000..b85825a --- /dev/null +++ b/test/fixtures/n6/encoding/positive/uint_4294967295.desc.json @@ -0,0 +1 @@ +{"source":"hand","value":{"i":"4294967295"}} diff --git a/test/fixtures/n6/encoding/positive/uint_4294967295.diag b/test/fixtures/n6/encoding/positive/uint_4294967295.diag new file mode 100644 index 0000000..4f6ff86 --- /dev/null +++ b/test/fixtures/n6/encoding/positive/uint_4294967295.diag @@ -0,0 +1 @@ +4294967295 diff --git a/test/fixtures/n6/encoding/positive/uint_4294967296.cbor b/test/fixtures/n6/encoding/positive/uint_4294967296.cbor new file mode 100644 index 0000000000000000000000000000000000000000..1374e5991ac04f66e243bf9f026cb3656e8c0866 GIT binary patch literal 9 Ocmb1UU|?VbVg>*K0RSBU literal 0 HcmV?d00001 diff --git a/test/fixtures/n6/encoding/positive/uint_4294967296.desc.json b/test/fixtures/n6/encoding/positive/uint_4294967296.desc.json new file mode 100644 index 0000000..45ee449 --- /dev/null +++ b/test/fixtures/n6/encoding/positive/uint_4294967296.desc.json @@ -0,0 +1 @@ +{"source":"hand","value":{"i":"4294967296"}} diff --git a/test/fixtures/n6/encoding/positive/uint_4294967296.diag b/test/fixtures/n6/encoding/positive/uint_4294967296.diag new file mode 100644 index 0000000..7df3e13 --- /dev/null +++ b/test/fixtures/n6/encoding/positive/uint_4294967296.diag @@ -0,0 +1 @@ +4294967296 diff --git a/test/fixtures/n6/encoding/positive/uint_65535.cbor b/test/fixtures/n6/encoding/positive/uint_65535.cbor new file mode 100644 index 0000000..456d681 --- /dev/null +++ b/test/fixtures/n6/encoding/positive/uint_65535.cbor @@ -0,0 +1 @@ +ÿÿ \ No newline at end of file diff --git a/test/fixtures/n6/encoding/positive/uint_65535.desc.json b/test/fixtures/n6/encoding/positive/uint_65535.desc.json new file mode 100644 index 0000000..85fe4cb --- /dev/null +++ b/test/fixtures/n6/encoding/positive/uint_65535.desc.json @@ -0,0 +1 @@ +{"source":"hand","value":{"i":"65535"}} diff --git a/test/fixtures/n6/encoding/positive/uint_65535.diag b/test/fixtures/n6/encoding/positive/uint_65535.diag new file mode 100644 index 0000000..7a53b35 --- /dev/null +++ b/test/fixtures/n6/encoding/positive/uint_65535.diag @@ -0,0 +1 @@ +65535 diff --git a/test/fixtures/n6/encoding/positive/uint_65536.cbor b/test/fixtures/n6/encoding/positive/uint_65536.cbor new file mode 100644 index 0000000000000000000000000000000000000000..f1c51ea841a7fee6feffa61ff8f013eaa4c65ef7 GIT binary patch literal 5 Mcmb1QU}Rtb004>r8~^|S literal 0 HcmV?d00001 diff --git a/test/fixtures/n6/encoding/positive/uint_65536.desc.json b/test/fixtures/n6/encoding/positive/uint_65536.desc.json new file mode 100644 index 0000000..d23bf91 --- /dev/null +++ b/test/fixtures/n6/encoding/positive/uint_65536.desc.json @@ -0,0 +1 @@ +{"source":"hand","value":{"i":"65536"}} diff --git a/test/fixtures/n6/encoding/positive/uint_65536.diag b/test/fixtures/n6/encoding/positive/uint_65536.diag new file mode 100644 index 0000000..e2ed8f4 --- /dev/null +++ b/test/fixtures/n6/encoding/positive/uint_65536.diag @@ -0,0 +1 @@ +65536 diff --git a/test/fixtures/n6/encoding/positive/uint_max_u64.cbor b/test/fixtures/n6/encoding/positive/uint_max_u64.cbor new file mode 100644 index 0000000..fed1e13 --- /dev/null +++ b/test/fixtures/n6/encoding/positive/uint_max_u64.cbor @@ -0,0 +1 @@ +ÿÿÿÿÿÿÿÿ \ No newline at end of file diff --git a/test/fixtures/n6/encoding/positive/uint_max_u64.desc.json b/test/fixtures/n6/encoding/positive/uint_max_u64.desc.json new file mode 100644 index 0000000..6191f02 --- /dev/null +++ b/test/fixtures/n6/encoding/positive/uint_max_u64.desc.json @@ -0,0 +1 @@ +{"source":"RFC 8949 Appendix A","value":{"i":"18446744073709551615"}} diff --git a/test/fixtures/n6/encoding/positive/uint_max_u64.diag b/test/fixtures/n6/encoding/positive/uint_max_u64.diag new file mode 100644 index 0000000..93221a2 --- /dev/null +++ b/test/fixtures/n6/encoding/positive/uint_max_u64.diag @@ -0,0 +1 @@ +18446744073709551615 diff --git a/test/fixtures/n6/header/negative/alg_missing/no_label_1.cbor b/test/fixtures/n6/header/negative/alg_missing/no_label_1.cbor new file mode 100644 index 0000000..1e0ee38 --- /dev/null +++ b/test/fixtures/n6/header/negative/alg_missing/no_label_1.cbor @@ -0,0 +1 @@ +¢X0tÌÎÊHY"•<ÍZèœ'p¤åËJäI)ó9Àìd9†)í¶�Z=3W`2ku¼x$application/vnd.example.test.v1+cose \ No newline at end of file diff --git a/test/fixtures/n6/header/negative/alg_missing/no_label_1.expect b/test/fixtures/n6/header/negative/alg_missing/no_label_1.expect new file mode 100644 index 0000000..3822c49 --- /dev/null +++ b/test/fixtures/n6/header/negative/alg_missing/no_label_1.expect @@ -0,0 +1 @@ +{"exit":1,"source":"hand","verdict_code":"header_not_protected"} diff --git a/test/fixtures/n6/header/negative/alg_not_mldsa87/eddsa.cbor b/test/fixtures/n6/header/negative/alg_not_mldsa87/eddsa.cbor new file mode 100644 index 0000000..b05451c --- /dev/null +++ b/test/fixtures/n6/header/negative/alg_not_mldsa87/eddsa.cbor @@ -0,0 +1 @@ +£'X0tÌÎÊHY"•<ÍZèœ'p¤åËJäI)ó9Àìd9†)í¶�Z=3W`2ku¼x$application/vnd.example.test.v1+cose \ No newline at end of file diff --git a/test/fixtures/n6/header/negative/alg_not_mldsa87/eddsa.expect b/test/fixtures/n6/header/negative/alg_not_mldsa87/eddsa.expect new file mode 100644 index 0000000..30fde88 --- /dev/null +++ b/test/fixtures/n6/header/negative/alg_not_mldsa87/eddsa.expect @@ -0,0 +1 @@ +{"exit":1,"source":"hand","verdict_code":"alg_not_permitted"} diff --git a/test/fixtures/n6/header/negative/alg_not_mldsa87/mldsa65.cbor b/test/fixtures/n6/header/negative/alg_not_mldsa87/mldsa65.cbor new file mode 100644 index 0000000..f9e1848 --- /dev/null +++ b/test/fixtures/n6/header/negative/alg_not_mldsa87/mldsa65.cbor @@ -0,0 +1 @@ +£80X0tÌÎÊHY"•<ÍZèœ'p¤åËJäI)ó9Àìd9†)í¶�Z=3W`2ku¼x$application/vnd.example.test.v1+cose \ No newline at end of file diff --git a/test/fixtures/n6/header/negative/alg_not_mldsa87/mldsa65.expect b/test/fixtures/n6/header/negative/alg_not_mldsa87/mldsa65.expect new file mode 100644 index 0000000..30fde88 --- /dev/null +++ b/test/fixtures/n6/header/negative/alg_not_mldsa87/mldsa65.expect @@ -0,0 +1 @@ +{"exit":1,"source":"hand","verdict_code":"alg_not_permitted"} diff --git a/test/fixtures/n6/header/negative/content_type/label_3.cbor b/test/fixtures/n6/header/negative/content_type/label_3.cbor new file mode 100644 index 0000000..50c4ecf --- /dev/null +++ b/test/fixtures/n6/header/negative/content_type/label_3.cbor @@ -0,0 +1 @@ +¤81axX0tÌÎÊHY"•<ÍZèœ'p¤åËJäI)ó9Àìd9†)í¶�Z=3W`2ku¼x$application/vnd.example.test.v1+cose \ No newline at end of file diff --git a/test/fixtures/n6/header/negative/content_type/label_3.expect b/test/fixtures/n6/header/negative/content_type/label_3.expect new file mode 100644 index 0000000..95877d9 --- /dev/null +++ b/test/fixtures/n6/header/negative/content_type/label_3.expect @@ -0,0 +1 @@ +{"exit":1,"source":"hand","verdict_code":"content_type_forbidden"} diff --git a/test/fixtures/n6/header/negative/crit/label_2.cbor b/test/fixtures/n6/header/negative/crit/label_2.cbor new file mode 100644 index 0000000..20954ca --- /dev/null +++ b/test/fixtures/n6/header/negative/crit/label_2.cbor @@ -0,0 +1 @@ +¤81�X0tÌÎÊHY"•<ÍZèœ'p¤åËJäI)ó9Àìd9†)í¶�Z=3W`2ku¼x$application/vnd.example.test.v1+cose \ No newline at end of file diff --git a/test/fixtures/n6/header/negative/crit/label_2.expect b/test/fixtures/n6/header/negative/crit/label_2.expect new file mode 100644 index 0000000..0ddfb9c --- /dev/null +++ b/test/fixtures/n6/header/negative/crit/label_2.expect @@ -0,0 +1 @@ +{"exit":1,"source":"hand","verdict_code":"header_label_forbidden"} diff --git a/test/fixtures/n6/header/negative/cwt_without_scitt_typ/cwt_on_plain_typ.cbor b/test/fixtures/n6/header/negative/cwt_without_scitt_typ/cwt_on_plain_typ.cbor new file mode 100644 index 0000000..c703d13 --- /dev/null +++ b/test/fixtures/n6/header/negative/cwt_without_scitt_typ/cwt_on_plain_typ.cbor @@ -0,0 +1 @@ +¤81X0tÌÎÊHY"•<ÍZèœ'p¤åËJäI)ó9Àìd9†)í¶�Z=3W`2ku¼¢cisscsubx$application/vnd.example.test.v1+cose \ No newline at end of file diff --git a/test/fixtures/n6/header/negative/cwt_without_scitt_typ/cwt_on_plain_typ.expect b/test/fixtures/n6/header/negative/cwt_without_scitt_typ/cwt_on_plain_typ.expect new file mode 100644 index 0000000..0ddfb9c --- /dev/null +++ b/test/fixtures/n6/header/negative/cwt_without_scitt_typ/cwt_on_plain_typ.expect @@ -0,0 +1 @@ +{"exit":1,"source":"hand","verdict_code":"header_label_forbidden"} diff --git a/test/fixtures/n6/header/negative/header_noncanonical/nonminimal_label.cbor b/test/fixtures/n6/header/negative/header_noncanonical/nonminimal_label.cbor new file mode 100644 index 0000000..0a2ba95 --- /dev/null +++ b/test/fixtures/n6/header/negative/header_noncanonical/nonminimal_label.cbor @@ -0,0 +1 @@ +£81X0tÌÎÊHY"•<ÍZèœ'p¤åËJäI)ó9Àìd9†)í¶�Z=3W`2ku¼x$application/vnd.example.test.v1+cose \ No newline at end of file diff --git a/test/fixtures/n6/header/negative/header_noncanonical/nonminimal_label.expect b/test/fixtures/n6/header/negative/header_noncanonical/nonminimal_label.expect new file mode 100644 index 0000000..502b1f9 --- /dev/null +++ b/test/fixtures/n6/header/negative/header_noncanonical/nonminimal_label.expect @@ -0,0 +1 @@ +{"exit":1,"source":"hand","verdict_code":"header_noncanonical"} diff --git a/test/fixtures/n6/header/negative/header_noncanonical/trailing_byte.cbor b/test/fixtures/n6/header/negative/header_noncanonical/trailing_byte.cbor new file mode 100644 index 0000000000000000000000000000000000000000..895814b3efb30abcfd386fa9bb3e760261165397 GIT binary patch literal 95 zcmV-l0HFV)0XQ)PSTJ9XY`4WDhxpDebm_ zT0JvY0bnv~b-WOGBw=uHY-wX*bZKvHFLrKZE@gOOZE$R5E_7vcbS`!=D`RhSWdKVI BC9ePg literal 0 HcmV?d00001 diff --git a/test/fixtures/n6/header/negative/header_noncanonical/trailing_byte.expect b/test/fixtures/n6/header/negative/header_noncanonical/trailing_byte.expect new file mode 100644 index 0000000..502b1f9 --- /dev/null +++ b/test/fixtures/n6/header/negative/header_noncanonical/trailing_byte.expect @@ -0,0 +1 @@ +{"exit":1,"source":"hand","verdict_code":"header_noncanonical"} diff --git a/test/fixtures/n6/header/negative/header_noncanonical/typ_before_kid.cbor b/test/fixtures/n6/header/negative/header_noncanonical/typ_before_kid.cbor new file mode 100644 index 0000000..b5add48 --- /dev/null +++ b/test/fixtures/n6/header/negative/header_noncanonical/typ_before_kid.cbor @@ -0,0 +1 @@ +£81x$application/vnd.example.test.v1+coseX0tÌÎÊHY"•<ÍZèœ'p¤åËJäI)ó9Àìd9†)í¶�Z=3W`2ku¼ \ No newline at end of file diff --git a/test/fixtures/n6/header/negative/header_noncanonical/typ_before_kid.expect b/test/fixtures/n6/header/negative/header_noncanonical/typ_before_kid.expect new file mode 100644 index 0000000..502b1f9 --- /dev/null +++ b/test/fixtures/n6/header/negative/header_noncanonical/typ_before_kid.expect @@ -0,0 +1 @@ +{"exit":1,"source":"hand","verdict_code":"header_noncanonical"} diff --git a/test/fixtures/n6/header/negative/kid_missing/no_label_4.cbor b/test/fixtures/n6/header/negative/kid_missing/no_label_4.cbor new file mode 100644 index 0000000..114f183 --- /dev/null +++ b/test/fixtures/n6/header/negative/kid_missing/no_label_4.cbor @@ -0,0 +1 @@ +¢81x$application/vnd.example.test.v1+cose \ No newline at end of file diff --git a/test/fixtures/n6/header/negative/kid_missing/no_label_4.expect b/test/fixtures/n6/header/negative/kid_missing/no_label_4.expect new file mode 100644 index 0000000..3822c49 --- /dev/null +++ b/test/fixtures/n6/header/negative/kid_missing/no_label_4.expect @@ -0,0 +1 @@ +{"exit":1,"source":"hand","verdict_code":"header_not_protected"} diff --git a/test/fixtures/n6/header/negative/kid_wrong_size/kid_32.cbor b/test/fixtures/n6/header/negative/kid_wrong_size/kid_32.cbor new file mode 100644 index 0000000..a58bf53 --- /dev/null +++ b/test/fixtures/n6/header/negative/kid_wrong_size/kid_32.cbor @@ -0,0 +1 @@ +£81X tÌÎÊHY"•<ÍZèœ'p¤åËJäI)ó9Àìdx$application/vnd.example.test.v1+cose \ No newline at end of file diff --git a/test/fixtures/n6/header/negative/kid_wrong_size/kid_32.expect b/test/fixtures/n6/header/negative/kid_wrong_size/kid_32.expect new file mode 100644 index 0000000..df2e1a7 --- /dev/null +++ b/test/fixtures/n6/header/negative/kid_wrong_size/kid_32.expect @@ -0,0 +1 @@ +{"exit":1,"source":"hand","verdict_code":"kid_not_thumbprint"} diff --git a/test/fixtures/n6/header/negative/label_0/label_0.cbor b/test/fixtures/n6/header/negative/label_0/label_0.cbor new file mode 100644 index 0000000000000000000000000000000000000000..a80dfaffd6afc57367e49939780e93a660ba8b7d GIT binary patch literal 96 zcmV-m0H6P)0003vF$7pJbj;4mND^5h6_q?6&06T3Cvc?Y%Sz-)Df1mU!0coXIfg0i zwt-qbGgkp%GHZ3b5O^eEaBysCV_|e@Z*DJkZe%WHcwudDY-KKVWpi{cb}=hsZ*yfQ C1tqWm literal 0 HcmV?d00001 diff --git a/test/fixtures/n6/header/negative/label_0/label_0.expect b/test/fixtures/n6/header/negative/label_0/label_0.expect new file mode 100644 index 0000000..0ddfb9c --- /dev/null +++ b/test/fixtures/n6/header/negative/label_0/label_0.expect @@ -0,0 +1 @@ +{"exit":1,"source":"hand","verdict_code":"header_label_forbidden"} diff --git a/test/fixtures/n6/header/negative/label_5/iv.cbor b/test/fixtures/n6/header/negative/label_5/iv.cbor new file mode 100644 index 0000000000000000000000000000000000000000..a91414c9725ed1b531571a084e83faca609c40e2 GIT binary patch literal 97 zcmV-n0G|J(0XQ)PSTJ9XY`4WDhxpDebm_ zT0JvY0bnv~b-V>Z01$X2VQ_G4X=7n@X>V>Xc5Y-YWq4t2aBO8RbY*jNE_N|1V{db1 DcFQI9 literal 0 HcmV?d00001 diff --git a/test/fixtures/n6/header/negative/label_5/iv.expect b/test/fixtures/n6/header/negative/label_5/iv.expect new file mode 100644 index 0000000..0ddfb9c --- /dev/null +++ b/test/fixtures/n6/header/negative/label_5/iv.expect @@ -0,0 +1 @@ +{"exit":1,"source":"hand","verdict_code":"header_label_forbidden"} diff --git a/test/fixtures/n6/header/negative/label_hash_envelope/label_258.cbor b/test/fixtures/n6/header/negative/label_hash_envelope/label_258.cbor new file mode 100644 index 0000000..7bdff36 --- /dev/null +++ b/test/fixtures/n6/header/negative/label_hash_envelope/label_258.cbor @@ -0,0 +1 @@ +¤81X0tÌÎÊHY"•<ÍZèœ'p¤åËJäI)ó9Àìd9†)í¶�Z=3W`2ku¼x$application/vnd.example.test.v1+cose8* \ No newline at end of file diff --git a/test/fixtures/n6/header/negative/label_hash_envelope/label_258.expect b/test/fixtures/n6/header/negative/label_hash_envelope/label_258.expect new file mode 100644 index 0000000..0ddfb9c --- /dev/null +++ b/test/fixtures/n6/header/negative/label_hash_envelope/label_258.expect @@ -0,0 +1 @@ +{"exit":1,"source":"hand","verdict_code":"header_label_forbidden"} diff --git a/test/fixtures/n6/header/negative/label_negative/minus_1.cbor b/test/fixtures/n6/header/negative/label_negative/minus_1.cbor new file mode 100644 index 0000000000000000000000000000000000000000..02ea3892ae34112428b4cbfeeab6dda87a472419 GIT binary patch literal 96 zcmV-m0H6P)0XQ)PSTJ9XY`4WDhxpDebm_ zT0JvY0bnv~b-WOGBw=uHY-wX*bZKvHFLrKZE@gOOZE$R5E_7vcbS`!=D`RhSWgq}? CTP4r{ literal 0 HcmV?d00001 diff --git a/test/fixtures/n6/header/negative/label_negative/minus_1.expect b/test/fixtures/n6/header/negative/label_negative/minus_1.expect new file mode 100644 index 0000000..0ddfb9c --- /dev/null +++ b/test/fixtures/n6/header/negative/label_negative/minus_1.expect @@ -0,0 +1 @@ +{"exit":1,"source":"hand","verdict_code":"header_label_forbidden"} diff --git a/test/fixtures/n6/header/negative/typ_missing/no_label_16.cbor b/test/fixtures/n6/header/negative/typ_missing/no_label_16.cbor new file mode 100644 index 0000000..38494ac --- /dev/null +++ b/test/fixtures/n6/header/negative/typ_missing/no_label_16.cbor @@ -0,0 +1 @@ +¢81X0tÌÎÊHY"•<ÍZèœ'p¤åËJäI)ó9Àìd9†)í¶�Z=3W`2ku¼ \ No newline at end of file diff --git a/test/fixtures/n6/header/negative/typ_missing/no_label_16.expect b/test/fixtures/n6/header/negative/typ_missing/no_label_16.expect new file mode 100644 index 0000000..3822c49 --- /dev/null +++ b/test/fixtures/n6/header/negative/typ_missing/no_label_16.expect @@ -0,0 +1 @@ +{"exit":1,"source":"hand","verdict_code":"header_not_protected"} diff --git a/test/fixtures/n6/header/negative/typ_not_in_table/other.cbor b/test/fixtures/n6/header/negative/typ_not_in_table/other.cbor new file mode 100644 index 0000000..4c2661c --- /dev/null +++ b/test/fixtures/n6/header/negative/typ_not_in_table/other.cbor @@ -0,0 +1 @@ +£81X0tÌÎÊHY"•<ÍZèœ'p¤åËJäI)ó9Àìd9†)í¶�Z=3W`2ku¼x%application/vnd.example.other.v1+cose \ No newline at end of file diff --git a/test/fixtures/n6/header/negative/typ_not_in_table/other.expect b/test/fixtures/n6/header/negative/typ_not_in_table/other.expect new file mode 100644 index 0000000..8e91206 --- /dev/null +++ b/test/fixtures/n6/header/negative/typ_not_in_table/other.expect @@ -0,0 +1 @@ +{"exit":1,"source":"hand","verdict_code":"typ_unexpected"} diff --git a/test/fixtures/n6/header/negative/typ_uint/typ_1.cbor b/test/fixtures/n6/header/negative/typ_uint/typ_1.cbor new file mode 100644 index 0000000..9dca2cf --- /dev/null +++ b/test/fixtures/n6/header/negative/typ_uint/typ_1.cbor @@ -0,0 +1 @@ +£81X0tÌÎÊHY"•<ÍZèœ'p¤åËJäI)ó9Àìd9†)í¶�Z=3W`2ku¼ \ No newline at end of file diff --git a/test/fixtures/n6/header/negative/typ_uint/typ_1.expect b/test/fixtures/n6/header/negative/typ_uint/typ_1.expect new file mode 100644 index 0000000..8e91206 --- /dev/null +++ b/test/fixtures/n6/header/negative/typ_uint/typ_1.expect @@ -0,0 +1 @@ +{"exit":1,"source":"hand","verdict_code":"typ_unexpected"} diff --git a/test/fixtures/n6/header/positive/alg_kid_cwt_typ_scitt.cbor b/test/fixtures/n6/header/positive/alg_kid_cwt_typ_scitt.cbor new file mode 100644 index 0000000..0458dd2 --- /dev/null +++ b/test/fixtures/n6/header/positive/alg_kid_cwt_typ_scitt.cbor @@ -0,0 +1 @@ +¤81X0tÌÎÊHY"•<ÍZèœ'p¤åËJäI)ó9Àìd9†)í¶�Z=3W`2ku¼¢cisscsubx*application/vnd.example.scitt-test.v1+cose \ No newline at end of file diff --git a/test/fixtures/n6/header/positive/alg_kid_typ.cbor b/test/fixtures/n6/header/positive/alg_kid_typ.cbor new file mode 100644 index 0000000..e81d79d --- /dev/null +++ b/test/fixtures/n6/header/positive/alg_kid_typ.cbor @@ -0,0 +1 @@ +£81X0tÌÎÊHY"•<ÍZèœ'p¤åËJäI)ó9Àìd9†)í¶�Z=3W`2ku¼x$application/vnd.example.test.v1+cose \ No newline at end of file diff --git a/test/fixtures/n6/header/profile-table.json b/test/fixtures/n6/header/profile-table.json new file mode 100644 index 0000000..42fe86a --- /dev/null +++ b/test/fixtures/n6/header/profile-table.json @@ -0,0 +1 @@ +{"note":"Trinity's test profile table for the header vectors (the draft leaves its CDDL Open, VB4).","provisional":true,"typ":[{"carrier":"single","scitt":false,"signers":"single","typ":"application/vnd.example.test.v1+cose"},{"carrier":"single","scitt":true,"signers":"single","typ":"application/vnd.example.scitt-test.v1+cose"}]} diff --git a/test/fixtures/n6/thumbprint/acvp_mldsa87_tg3_tc51.kid b/test/fixtures/n6/thumbprint/acvp_mldsa87_tg3_tc51.kid new file mode 100644 index 0000000..65542d1 --- /dev/null +++ b/test/fixtures/n6/thumbprint/acvp_mldsa87_tg3_tc51.kid @@ -0,0 +1 @@ +tÌÎÊHY"•<ÍZèœ'p¤åËJäI)ó9Àìd9†)í¶�Z=3W`2ku¼ \ No newline at end of file diff --git a/test/fixtures/n6/thumbprint/acvp_mldsa87_tg3_tc51.pub b/test/fixtures/n6/thumbprint/acvp_mldsa87_tg3_tc51.pub new file mode 100644 index 0000000000000000000000000000000000000000..f0e13fe3a91684408cad6683a72661af4d580271 GIT binary patch literal 2592 zcmV+*3g7jmcz%FHB#%FZs>M!}Y5bV+u@j8JC$S*;lx%>xZas<2{y52%WL-;YRi3P} zuecT=*JZ}!E^`ANM9b$*GAw%*B$amSOi%(-Q;ixdYe0@@8uXh#x#A!>+^`xZ9Lb`}<(IGj18BvzH(;K*#xiaW)n4yOIQz@tQ0)ZYFz_jSdY zHifS8BYZKDDq)}a1h43>)s{`h8MbWPoi**Jque3K zDY=r6B-@LHE&cHVpoWB30{tYOPt69oW68ih{((x@wsoWgFl2j|yVd+y0;+)G$5_Q`B_J5!F-0zTp*V7uPDnrp(0b z9Cuwo?q&`og``^br^*OD-ON7Z|NLHex|hsEb1*!2*tY$%UMe7Bd+WY3ebUjbABYU7 z>fzu9dXSTno?5YuKx$Er-HaX&3b1!J^nfl;mFHts`c=W)D{uSML#r0s# zDE0SW;G>#grW)^TeSjfB>42Vk%4nmD>Am$aB3N)wixICvVj>K+Rzd!i--GO*cx`TJ zAS{vWOsY;eq8vQN3BrqyWmPZeGe}T{i@PwmH`sKBeyuYKA6rd-YwSlzzEr1z)v_8H zE5HhWB0&4u07eIe(U%&O#fM2fA%iobw-^Q3ae+VHGG zkg4eSjR&re?YN|}7{lwmzEg-Nf%4u1OBeqBX)r5Eb8LmTHi0Wm$OH`-Yl(!~u;K<8u&}EaVOcE`G}5Ba}J=03dhM z*=&1EKe6J;?wnF0rZQ6UV{*JJ*+mD=S18W6psF)ceg9v31+Ljy>$xPhnDAwK^giy64OG!wlTi{0s;S`uQNMw$i$;BquHvLXcq}(B zRi-Td^ay}HOFP)_xjSNbzTj z_iTJ1`5M>`AwD^+iIytrMBr|#H5{8dSXtGmgx(C-XC@4`3YPT<#w&9WN6Q~PrmiS- zCvo)bJLFyU0PqtlZj^l_XsWqE3&Hh)GK6unBECP%({gs|VU!0Ox517kCsI#3vc7x_ zZdvv%9|v9;QQb3A_R5!H@YYZpL(1!LIq8>yOXqv zN7kFCk+P-Ml&E)WBDjbfveA;O3PrBR8gX&>MB}l<6rB-{`iY*1*=i|mUbqpjk~;kw zlHkV_WVsBEUcyOK{*0UZQHn5&Jun@OY+~iDK=wP%Bx#!PyVB&3*{Xax99efUa1K3& z;cC-rXS@R&^MSZ5^a~p!Ur};-6T8C^p@#j!KH_I{mq5%{!)xGQlQHT*C!93IPq_X3 z$LYt-``OnMo;kPSq?1S^A+a2Y8>!Sxy1u4Q z8u5%6v$gq;^r>Ij2jLQ~*jKPKyM7P(Mr`%`X&lTELch4qfx6e*RYVE5o+Hqt5MzaY zN}yGPQeJ88ssVp$f2nLGqmFug0pc%~t-EMDPPn9pAS5Q%jVXIDz6yXnPs$zxeY3q| zqVR70XuVB)>ew@lHmnpS7CSt1n})2NmVVVu;!LHB!70!8HHEfk+%xkl#H88b1_x+K zyd`w-<*O_UT)n)do3=!}hVwIALH#r zgN3S}+m4i{?pssx%rUd4y2pp*O1V>S#40{EkVD4x!5t5MMJQ6k`#%^(lp%%$LxuaE zNGW2m+UwG@-x6IKVT3<%qH^P**?Oy&-dHikZ5F1cVh@aL(Dd=-8vbaNx;A9eIs@vn zQxPWUb2aq5yYl0lN42Gkp-^V`mlxP6W@lAC%?Xk5O+xsSO?5db@kZpejR2$I6P;#y C5*#T2 literal 0 HcmV?d00001 diff --git a/test/support/n6_vectors.ex b/test/support/n6_vectors.ex new file mode 100644 index 0000000..64123b7 --- /dev/null +++ b/test/support/n6_vectors.ex @@ -0,0 +1,87 @@ +# SPDX-FileCopyrightText: Sudo Apt Holdings LLC +# SPDX-License-Identifier: Apache-2.0 +defmodule Trinity.TestSupport.N6Vectors do + @moduledoc """ + Reads Trinity's provisional N6 vectors (`test/fixtures/n6/`, written by + `scripts/n6_vectors.exs`; slice 138). The populations every N6 test iterates are listed from + the directory, never typed, so a vector added to the set is tested without a test changing. + """ + + @root "test/fixtures/n6" + + @doc "The vector directory." + def root, do: @root + + @doc "`{id, bytes, value}` for each positive vector of a layer." + def positive(layer) do + for desc_path <- + Path.wildcard(Path.join([@root, layer, "positive", "*.desc.json"])) |> Enum.sort() do + id = Path.basename(desc_path, ".desc.json") + bytes = File.read!(Path.join([@root, layer, "positive", id <> ".cbor"])) + %{"value" => desc} = desc_path |> File.read!() |> JSON.decode!() + {id, bytes, value(desc)} + end + end + + @doc "`{defect, id, bytes, code}` for each negative vector of a layer." + def negative(layer) do + for expect_path <- + Path.wildcard(Path.join([@root, layer, "negative", "*", "*.expect"])) |> Enum.sort() do + defect = expect_path |> Path.dirname() |> Path.basename() + id = Path.basename(expect_path, ".expect") + bytes = File.read!(Path.rootname(expect_path) <> ".cbor") + %{"exit" => 1, "verdict_code" => code} = expect_path |> File.read!() |> JSON.decode!() + {defect, id, bytes, String.to_existing_atom(code)} + end + end + + @doc "The value a vector description stands for (integers are decimal strings, so above 2^53 survive JSON)." + def value(%{"i" => n}), do: String.to_integer(n) + def value(%{"b" => hex}), do: {:bytes, Base.decode16!(hex, case: :lower)} + def value(%{"t" => hex}), do: {:text, Base.decode16!(hex, case: :lower)} + def value(%{"s" => "true"}), do: true + def value(%{"s" => "false"}), do: false + def value(%{"s" => "null"}), do: nil + def value(%{"a" => items}), do: Enum.map(items, &value/1) + def value(%{"m" => pairs}), do: Map.new(pairs, fn [k, v] -> {value(k), value(v)} end) + + @doc "The body vectors' test schema, as `Trinity.N6.Body` takes it." + def body_schema do + s = Path.join([@root, "body", "schema.json"]) |> File.read!() |> JSON.decode!() + + fields = + Map.new(s["fields"], fn f -> + type = + case f["type"] do + "tstr" -> :tstr + "uint" -> :uint + "set of uint" -> {:set, :uint} + end + + {f["key"], + {String.to_atom(f["name"]), type, if(f["required"], do: :required, else: :optional)}} + end) + + %{kind: s["kind"], version: s["schema_version"], fields: fields} + end + + @doc "The header vectors' `typ` table, as `Trinity.N6.COSE` takes it." + def typ_table do + Path.join([@root, "header", "profile-table.json"]) + |> File.read!() + |> JSON.decode!() + |> Map.fetch!("typ") + |> Map.new(&{&1["typ"], %{scitt: &1["scitt"]}}) + end + + @doc "NIST ACVP's ML-DSA-87 keyGen case (slice 137 pins it): seed, public key, expanded secret key." + def acvp_key do + k = "priv/kat/acvp/mldsa87_keygen.json" |> File.read!() |> JSON.decode!() + + %{ + seed: Base.decode16!(k["seed"]), + public: Base.decode16!(k["pk"]), + expanded: Base.decode16!(k["sk"]) + } + end +end diff --git a/test/test_helper.exs b/test/test_helper.exs index eec45ad..a1bd93f 100644 --- a/test/test_helper.exs +++ b/test/test_helper.exs @@ -15,6 +15,8 @@ # :mldsa87_runtime (planted faults in the ML-DSA-87 known-answer check, which can only be seen # where the runtime signs) runs where the oracle says it does. That is derived, not declared, so # the decision is printed: a leg that loses ML-DSA-87 says so in its own log. +# Slice 138: :n6_corpus (10^6 generated N6 bodies and their mutants) is opt-in, run with +# `mix test --only n6_corpus`; it is a measurement, too long for every run. fips_leg? = System.get_env("TRINITY_FIPS_LEG") == "1" local_model? = System.get_env("TRINITY_LOCAL_MODEL_CACHE") not in [nil, ""] below_floor_otp? = System.get_env("TRINITY_BELOW_FLOOR_OTP") not in [nil, ""] @@ -35,7 +37,7 @@ IO.puts( ExUnit.start( exclude: - [:live, :desktop, :eval] ++ + [:live, :desktop, :eval, :n6_corpus] ++ if(fips_leg?, do: [], else: [:fips]) ++ if(local_model?, do: [], else: [:local_model]) ++ if(below_floor_otp?, do: [], else: [:below_floor_otp]) ++ diff --git a/test/trinity/n6/body_digest_test.exs b/test/trinity/n6/body_digest_test.exs new file mode 100644 index 0000000..ac1efa5 --- /dev/null +++ b/test/trinity/n6/body_digest_test.exs @@ -0,0 +1,103 @@ +# SPDX-FileCopyrightText: Sudo Apt Holdings LLC +# SPDX-License-Identifier: Apache-2.0 +defmodule Trinity.N6.BodyDigestTest do + @moduledoc """ + Slice 138, AC4 and AC5: the body rules under the vectors' test schema, and digests of foreign + bytes. Both read their populations from `test/fixtures/n6/`. + """ + + use ExUnit.Case, async: true + + alias Trinity.N6.{Body, CBOR, Digest, Profile} + alias Trinity.TestSupport.N6Vectors + + @kind "trinity.test" + + setup_all do + %{schemas: [N6Vectors.body_schema()]} + end + + describe "AC4: the body rules" do + test "every positive body decodes under its schema", %{schemas: schemas} do + vectors = N6Vectors.positive("body") + assert vectors != [] + + for {id, bytes, value} <- vectors do + assert {:ok, ^value, %{version: 1}} = Body.decode(bytes, @kind, schemas), id + assert Body.encode(value, @kind, schemas) == {:ok, bytes}, id + end + end + + test "every negative body is refused with its code", %{schemas: schemas} do + vectors = N6Vectors.negative("body") + assert length(vectors) >= 12 + + for {defect, id, bytes, code} <- vectors do + assert {:error, ^code, _} = Body.decode(bytes, @kind, schemas), "#{defect}/#{id}" + assert Profile.exit_of(code) == 1 + end + end + + test "the encoder refuses a body the decoder would refuse", %{schemas: schemas} do + assert Body.encode(%{16 => {:text, "x"}}, @kind, schemas) == + {:error, :schema_version_missing} + + assert Body.encode(%{0 => 1, 16 => {:text, "x"}, 18 => [2, 1]}, @kind, schemas) == + {:error, :set_not_canonical} + + assert Body.encode(%{0 => 1, 16 => {:text, "x"}, 99 => 0}, @kind, schemas) == + {:error, :unknown_key} + end + + test "a nested map is checked against its own fields, key 0 included" do + schemas = [ + %{ + kind: "k", + version: 1, + fields: %{16 => {:inner, {:map, %{1 => {:a, :uint, :required}}}, :required}} + } + ] + + assert {:ok, _} = Body.check(%{0 => 1, 16 => %{1 => 5}}, "k", schemas) + + assert Body.check(%{0 => 1, 16 => %{0 => 0, 1 => 5}}, "k", schemas) == + {:error, :unknown_key} + + assert Body.check(%{0 => 1, 16 => %{}}, "k", schemas) == {:error, :required_key_missing} + end + + test "a schema version in the reserved carrier range is refused even when a schema claims it" do + schemas = [%{kind: @kind, version: 0xFFFF_0001, fields: %{}}] + assert Body.check(%{0 => 0xFFFF_0001}, @kind, schemas) == {:error, :schema_reserved} + end + end + + describe "AC5: digests of foreign bytes" do + test "the vector: SHA-384 over the bytes exactly as given, and their exact length" do + raw = File.read!(Path.join(N6Vectors.root(), "digest/abc.in")) + expected = File.read!(Path.join(N6Vectors.root(), "digest/abc.ref.cbor")) + + ref = Digest.foreign_ref(raw, "application/octet-stream") + assert CBOR.encode!(ref) == expected + assert %{0 => digest, 1 => 3} = ref + assert Digest.check(digest) == :ok + assert Digest.matches?(digest, raw) + refute Digest.matches?(digest, raw <> "\n") + end + + test "every negative digest is refused with its code" do + vectors = N6Vectors.negative("digest") + assert length(vectors) >= 4 + + for {defect, id, bytes, code} <- vectors do + assert {:ok, value} = CBOR.decode(bytes), "#{defect}/#{id} must be valid CBOR" + assert Digest.check(value) == {:error, code}, "#{defect}/#{id}" + end + end + + test "SHA-512 is refused until the profile decides it" do + assert Digest.check([-44, {:bytes, :binary.copy(<<0>>, 64)}]) == + {:error, :digest_alg_not_permitted} + end + end +end diff --git a/test/trinity/n6/cbor_test.exs b/test/trinity/n6/cbor_test.exs new file mode 100644 index 0000000..7bf7e7c --- /dev/null +++ b/test/trinity/n6/cbor_test.exs @@ -0,0 +1,192 @@ +# SPDX-FileCopyrightText: Sudo Apt Holdings LLC +# SPDX-License-Identifier: Apache-2.0 +defmodule Trinity.N6.CBORTest do + @moduledoc """ + Slice 138, AC1 to AC3: the N6 codec against Trinity's provisional vectors (`test/fixtures/n6/`), + RFC 8949 Appendix A where the subset reaches, a property over generated values, and the limits. + The vectors' bytes are hex literals in `scripts/n6_vectors.exs`, not output of this encoder. + """ + + use ExUnit.Case, async: true + use ExUnitProperties + + alias Trinity.N6.{CBOR, Profile} + alias Trinity.TestSupport.N6Vectors + + @limits Profile.limits() + + describe "AC1: positive vectors" do + test "every positive vector encodes to its bytes and decodes to its value" do + vectors = N6Vectors.positive("encoding") + assert length(vectors) > 40, "only #{length(vectors)} positive vectors found" + + for {id, bytes, value} <- vectors do + assert CBOR.encode(value) == {:ok, bytes}, "#{id}: encoding differs" + assert CBOR.decode(bytes) == {:ok, value}, "#{id}: decoding differs" + end + end + + test "the RFC 8949 Appendix A examples inside the subset are among them" do + from_rfc = + for path <- Path.wildcard(Path.join(N6Vectors.root(), "encoding/positive/*.desc.json")), + path |> File.read!() |> JSON.decode!() |> Map.fetch!("source") == + "RFC 8949 Appendix A", + do: path + + assert length(from_rfc) >= 30 + end + + property "every generated value round-trips" do + check all(value <- n6_value()) do + assert {:ok, bytes} = CBOR.encode(value) + assert CBOR.decode(bytes) == {:ok, value} + end + end + + property "every byte string the decoder accepts re-encodes to itself" do + check all( + value <- n6_value(), + bytes = CBOR.encode!(value), + at <- StreamData.integer(0..(byte_size(bytes) - 1)), + xor <- StreamData.integer(1..255) + ) do + <> = bytes + mutant = <> + + case CBOR.decode(mutant) do + {:ok, decoded} -> assert CBOR.encode(decoded) == {:ok, mutant} + {:error, code, _offset} -> assert is_map_key(Profile.codes(), code) + end + end + end + end + + describe "AC2: negative vectors" do + test "every negative vector is refused with its code" do + vectors = N6Vectors.negative("encoding") + assert length(vectors) > 50, "only #{length(vectors)} negative vectors found" + + # Every mismatch is collected, so a planted defect shows which classes it turned red. + wrong = + for {defect, id, bytes, code} <- vectors, + answer = CBOR.decode(bytes), + not match?({:error, ^code, _}, answer), + do: "#{defect}/#{id}: #{inspect(answer)}, expected #{code}" + + assert wrong == [], "refused wrongly or accepted:\n" <> Enum.join(wrong, "\n") + assert Enum.all?(vectors, fn {_, _, _, code} -> Profile.exit_of(code) == 1 end) + end + + test "nested inside an array and a map value, each is refused with the same code" do + for {defect, id, bytes, code} <- N6Vectors.negative("encoding") do + assert {:error, ^code, _} = CBOR.decode(<<0x81>> <> bytes), "#{defect}/#{id} in an array" + + assert {:error, ^code, _} = CBOR.decode(<<0xA1, 0x00>> <> bytes), + "#{defect}/#{id} as a map value" + end + end + + test "every refusal is found by the one pass, none is left to the round trip" do + for {defect, id, bytes, code} <- N6Vectors.negative("encoding") do + assert {:error, ^code, _} = CBOR.pass(bytes), "#{defect}/#{id}: only E11 refused it" + end + end + + test "the encoder refuses what the decoder refuses, by the same code" do + assert CBOR.encode(1.0) == {:error, :float_forbidden} + assert CBOR.encode(-0x8000_0000_0000_0001) == {:error, :int_out_of_range} + assert CBOR.encode(0x1_0000_0000_0000_0000) == {:error, :int_out_of_range} + assert CBOR.encode(%{-1 => 0}) == {:error, :key_not_uint} + assert CBOR.encode(%{0x1_0000_0000 => 0}) == {:error, :key_not_uint} + assert CBOR.encode(%{"a" => 0}) == {:error, :key_not_uint} + assert CBOR.encode({:text, "eÌ�"}) == {:error, :not_nfc} + assert CBOR.encode({:text, <<0xFF>>}) == {:error, :invalid_utf8} + assert CBOR.encode("bare") == {:error, :value_not_n6} + assert CBOR.encode(:atom) == {:error, :value_not_n6} + end + end + + describe "AC3: limits, at the limit and one past it" do + test "depth" do + assert {:ok, _} = CBOR.decode(String.duplicate(<<0x81>>, @limits.depth - 1) <> <<0x80>>) + + assert {:error, :too_deep, _} = + CBOR.decode(String.duplicate(<<0x81>>, @limits.depth) <> <<0x80>>) + end + + test "items, the top-level item and map keys included" do + at = @limits.items - 1 + assert {:ok, list} = CBOR.decode(<<0x99, at::16>> <> :binary.copy(<<0>>, at)) + assert length(list) == at + + past = @limits.items + + assert {:error, :too_many_items, _} = + CBOR.decode(<<0x9A, past::32>> <> :binary.copy(<<0>>, past)) + + # A map of n pairs is 2n + 1 items. + pairs = div(@limits.items - 1, 2) + + map = + <<0xB9, pairs::16>> <> + IO.iodata_to_binary(for k <- 0..(pairs - 1), do: [CBOR.encode!(k), 0]) + + assert {:ok, decoded} = CBOR.decode(map) + assert map_size(decoded) == pairs + end + + test "text, in UTF-8 bytes" do + at = @limits.text + assert {:ok, {:text, _}} = CBOR.decode(<<0x7A, at::32>> <> :binary.copy("a", at)) + + past = at + 1 + + assert {:error, :text_too_long, 0} = + CBOR.decode(<<0x7A, past::32>> <> :binary.copy("a", past)) + end + + test "size, the whole input" do + at = @limits.size - 5 + assert {:ok, {:bytes, b}} = CBOR.decode(<<0x5A, at::32>> <> :binary.copy(<<0>>, at)) + assert byte_size(b) == at + + assert {:error, :too_large, 0} = + CBOR.decode(<<0x5A, at + 1::32>> <> :binary.copy(<<0>>, at + 1)) + end + + test "a forged count larger than the input is refused before anything is read" do + assert {:error, :truncated, 0} = CBOR.decode(<<0x9B, 0xFFFF_FFFF_FFFF_FFFF::64>>) + assert {:error, :truncated, 0} = CBOR.decode(<<0xBB, 0xFFFF_FFFF_FFFF_FFFF::64>>) + end + end + + # Values in the subset. Text is printable strings normalised to NFC, which is what an encoder + # must be given; the checked-in vectors, not this generator, keep to older code points. + defp n6_value do + leaf = + StreamData.one_of([ + StreamData.integer(-0x8000_0000_0000_0000..0xFFFF_FFFF_FFFF_FFFF), + StreamData.integer(-30..30), + StreamData.map(StreamData.binary(max_length: 40), &{:bytes, &1}), + StreamData.map( + StreamData.string(:printable, max_length: 20), + &{:text, :unicode.characters_to_nfc_binary(&1)} + ), + StreamData.member_of([true, false, nil]) + ]) + + key = StreamData.one_of([StreamData.integer(0..30), StreamData.integer(0..0xFFFF_FFFF)]) + + StreamData.tree(leaf, fn child -> + StreamData.one_of([ + StreamData.list_of(child, max_length: 5), + StreamData.map_of(key, child, max_length: 5) + ]) + end) + |> StreamData.filter(&(depth(&1) <= @limits.depth)) + end + + defp depth(v) when is_list(v), do: 1 + Enum.reduce(v, 0, &max(depth(&1), &2)) + defp depth(v) when is_map(v), do: 1 + Enum.reduce(Map.values(v), 0, &max(depth(&1), &2)) + defp depth(_v), do: 0 +end diff --git a/test/trinity/n6/census_test.exs b/test/trinity/n6/census_test.exs new file mode 100644 index 0000000..019e5f5 --- /dev/null +++ b/test/trinity/n6/census_test.exs @@ -0,0 +1,117 @@ +# SPDX-FileCopyrightText: Sudo Apt Holdings LLC +# SPDX-License-Identifier: Apache-2.0 +defmodule Trinity.N6.CensusTest do + @moduledoc """ + Slice 138, AC9: every place in `lib/` that hashes or signs, classified. + + The rule `Trinity.N6` serves has two halves: what this tree constructs and signs is N6, and bytes + someone else produced are hashed exactly as received. This census holds every call site that + could break either half, so a new one cannot arrive unread. The population is derived from the + tree: every `.ex` file under `lib/`, scanned for the calls in `@calls`. A file whose count differs + from its row, or a file with no row, fails here by name. + + Classes: + + * `:n6` - this boundary; + * `:exact_bytes` - bytes hashed exactly as read or received; + * `:identifier` - a local identifier, key derivation or comparison, not a statement and not a + reference to someone else's bytes; + * `:protocol` - a format an external protocol fixes (PKCE S256, an OAuth access token's JWS); + * `:local_artifact` - a digest of this tree's own build output; + * `:in_memory` - within one run, never written or signed; + * `:test_double` - a fake used by the suite; + * `:legacy_signed` - receipts and checkpoints as they are signed today (DSSE's + pre-authentication encoding over RFC 8785 JSON, SHA-256 chains), which move to N6 with the + receipts slice and the per-box log; + * `:reserialized` - foreign data re-encoded before it is hashed, which the rule forbids. Each + is declared here with what it would take to hash the exact bytes instead; none changes in + this slice. + """ + + use ExUnit.Case, async: true + + @calls ~r/:crypto\.(hash|hash_init|sign|verify|mac|mac_init)\(|Jcs\.encode\(|JOSE\.JWS\.sign\(|Envelope\.pae\(/ + + # path => {calls in that file, class, why} + @census %{ + "lib/trinity/n6/cose.ex" => {3, :n6, "thumbprint, ML-DSA-87 sign and verify"}, + "lib/trinity/n6/digest.ex" => {1, :n6, "SHA-384 over foreign bytes exactly as given"}, + "lib/trinity/archive.ex" => {1, :exact_bytes, "the archive file's bytes"}, + "lib/mix/tasks/trinity.image.inspect.ex" => {2, :exact_bytes, "image layer and file bytes"}, + "lib/trinity/skills/parser.ex" => {1, :exact_bytes, "a skill file's bytes"}, + "lib/trinity/skills/staging.ex" => {1, :exact_bytes, "staged skill files' bytes"}, + "lib/trinity_web/controllers/auth_controller.ex" => + {2, :identifier, "constant-time comparison of a token's digest"}, + "lib/trinity/keys/local.ex" => {2, :identifier, "a key id and HKDF expansion"}, + "lib/mix/tasks/trinity.names.ex" => + {1, :identifier, "salted digests of names the check refuses"}, + "lib/trinity/content/part.ex" => {1, :identifier, "a message part's identity"}, + "lib/trinity/mcp/auth/client/store.ex" => {1, :identifier, "a storage key"}, + "lib/trinity/memory/observer.ex" => {1, :identifier, "a memory entry's key suffix"}, + "lib/trinity/tools/fs.ex" => {1, :identifier, "a path's identity"}, + "lib/trinity/mcp/auth/embedded.ex" => + {2, :protocol, "PKCE S256 (RFC 7636) and the access token's JWS"}, + "lib/trinity/mcp/auth/client.ex" => {1, :protocol, "PKCE S256 (RFC 7636)"}, + "lib/trinity/core_policy.ex" => {1, :local_artifact, "the policy modules' stripped beams"}, + "lib/mix/tasks/trinity.effects.catalog.ex" => {1, :local_artifact, "the generated catalogue"}, + "lib/trinity/tools/registry.ex" => {1, :in_memory, "term_to_binary digest within one run"}, + "lib/trinity/memory/embedders/fake.ex" => {1, :test_double, "the fake embedder's vectors"}, + "lib/trinity/receipts/envelope.ex" => + {2, :legacy_signed, "RFC 8785 body and SHA-256 chain hash"}, + "lib/trinity/receipts/chain_writer.ex" => + {6, :legacy_signed, "DSSE PAE for receipts and checkpoints"}, + "lib/trinity/receipts/verifier.ex" => {2, :legacy_signed, "DSSE PAE, verified"}, + "lib/trinity/receipts/merkle.ex" => {3, :legacy_signed, "the checkpoint tree, SHA-256"}, + "lib/trinity/receipts/signer.ex" => {1, :legacy_signed, "RFC 7638 key id"}, + "lib/trinity/receipts/key_custody.ex" => + {5, :legacy_signed, "receipt key ids and fingerprints"}, + "lib/trinity/receipts/signer/ed25519.ex" => {2, :legacy_signed, "Ed25519 sign and verify"}, + "lib/trinity/receipts/signer/p384.ex" => {2, :legacy_signed, "ECDSA P-384 sign and verify"}, + "lib/trinity/receipts/signer/mldsa87.ex" => {2, :legacy_signed, "ML-DSA-87 sign and verify"}, + "lib/trinity/tools/definition_digest.ex" => + {2, :reserialized, + "a server's tool listing, rebuilt as RFC 8785 JSON; the exact bytes are the listing response, which the MCP client layer does not expose"}, + "lib/trinity/permissions/fingerprint.ex" => + {2, :reserialized, + "the model's call arguments as RFC 8785 JSON; they can hold floats, so the exact bytes of the call are the candidate"}, + "lib/trinity/mcp/server/envelope.ex" => + {1, :reserialized, + "a client's call arguments re-encoded with sorted keys; the exact request bytes are the candidate"}, + "lib/trinity/effects.ex" => + {1, :reserialized, + "tool output: exact for a binary, `inspect/1` otherwise, which is not stable across Elixir versions"} + } + + @classes ~w(n6 exact_bytes identifier protocol local_artifact in_memory test_double legacy_signed reserialized)a + + test "every hashing and signing call in lib/ is in the census, with its count and a class" do + found = + for path <- Path.wildcard("lib/**/*.ex"), + count = length(Regex.scan(@calls, File.read!(path))), + count > 0, + into: %{}, + do: {path, count} + + expected = Map.new(@census, fn {path, {count, _class, _why}} -> {path, count} end) + + unclassified = + for {path, n} <- found, Map.get(expected, path) != n, do: "#{path}: #{n} call(s)" + + gone = + for {path, n} <- expected, + not is_map_key(found, path), + do: "#{path}: row for #{n}, none found" + + assert unclassified == [], + "calls the census does not hold:\n" <> Enum.join(unclassified, "\n") + + assert gone == [], "rows with no calls left:\n" <> Enum.join(gone, "\n") + end + + test "every row names a known class and says why" do + for {path, {_count, class, why}} <- @census do + assert class in @classes, "#{path}: unknown class #{inspect(class)}" + assert is_binary(why) and why != "", "#{path}: no reason given" + end + end +end diff --git a/test/trinity/n6/corpus_test.exs b/test/trinity/n6/corpus_test.exs new file mode 100644 index 0000000..6af813f --- /dev/null +++ b/test/trinity/n6/corpus_test.exs @@ -0,0 +1,130 @@ +# SPDX-FileCopyrightText: Sudo Apt Holdings LLC +# SPDX-License-Identifier: Apache-2.0 +defmodule Trinity.N6.CorpusTest do + @moduledoc """ + Slice 138, AC10, opt-in (`mix test --only n6_corpus`): a corpus of generated bodies round-trips + byte for byte, and every mutant the operators below make of them is refused with the operator's + code. + + `TRINITY_N6_CORPUS` sets the number of bodies (default 1,000,000); the random source is seeded, + so a run can be repeated. The claim is about this operator population, not about every possible + input: a defect class no operator makes is not covered, and a new class becomes a new operator. + """ + + use ExUnit.Case, async: true + + import Bitwise, only: [<<<: 2] + + alias Trinity.N6.CBOR + + @moduletag :n6_corpus + @moduletag timeout: :infinity + + # name => the code its mutants must be refused with + @operators %{ + nonminimal_head: :noncanonical, + indefinite_length: :indefinite_length, + float_value: :float_forbidden, + tag_value: :tag_forbidden, + simple_value: :simple_forbidden, + text_key: :key_not_uint, + negative_key: :key_not_uint, + key_over_u32: :key_not_uint, + swap_keys: :noncanonical, + duplicate_key: :duplicate_key, + trailing_bytes: :trailing_bytes, + truncated: :truncated, + not_nfc: :not_nfc + } + + test "generated bodies round-trip and every mutant is refused with its operator's code" do + n = String.to_integer(System.get_env("TRINITY_N6_CORPUS", "1000000")) + :rand.seed(:exsss, {138, 10, 1}) + + counts = + Enum.reduce(1..n, %{bodies: 0}, fn i, counts -> + body = body(i) + bytes = CBOR.encode!(body) + assert CBOR.decode(bytes) == {:ok, body} + + pairs = for k <- Enum.sort(Map.keys(body)), do: {CBOR.encode!(k), CBOR.encode!(body[k])} + + Enum.reduce(@operators, Map.update!(counts, :bodies, &(&1 + 1)), fn {name, code}, + counts -> + case mutate(name, pairs, bytes) do + nil -> + counts + + mutant -> + case CBOR.decode(mutant) do + {:error, ^code, _} -> + Map.update(counts, name, 1, &(&1 + 1)) + + other -> + flunk("#{name} on #{Base.encode16(bytes)}: #{inspect(other)}, expected #{code}") + end + end + end) + end) + + mutants = counts |> Map.delete(:bodies) |> Map.values() |> Enum.sum() + + IO.puts( + "n6 corpus: #{counts.bodies} bodies round-tripped; #{mutants} of #{mutants} mutants refused" + ) + + for {name, count} <- counts |> Map.delete(:bodies) |> Enum.sort(), + do: IO.puts(" #{name}: #{count}") + + assert map_size(counts) == map_size(@operators) + 1, "an operator made no mutant" + end + + # A body: key 0 the version, then one to six fields of mixed types under 16..1030. + defp body(i) do + fields = + for k <- Enum.take_random(16..1030, :rand.uniform(6)), into: %{}, do: {k, value(k + i)} + + Map.put(fields, 0, rem(i, 7) + 1) + end + + defp value(x) do + case rem(x, 6) do + 0 -> :rand.uniform(1 <<< 40) - (1 <<< 39) + 1 -> {:bytes, :rand.bytes(:rand.uniform(40) - 1)} + 2 -> {:text, Enum.random(["", "a", "café", "æ°´", "op-#{x}"])} + 3 -> Enum.map(1..:rand.uniform(4), fn _ -> :rand.uniform(1000) end) + 4 -> Enum.random([true, false, nil]) + 5 -> %{1 => :rand.uniform(100), 2 => {:text, "n"}} + end + end + + # Every body has fewer than 23 pairs, so its map head is one byte, 0xa0 + n. + defp mutate(:nonminimal_head, pairs, _bytes), do: <<0xB8, length(pairs)>> <> join(pairs) + defp mutate(:indefinite_length, pairs, _bytes), do: <<0xBF>> <> join(pairs) <> <<0xFF>> + + defp mutate(:float_value, [{k0, _v0} | rest], _bytes), + do: map([{k0, <<0xF9, 0x3C, 0x00>>} | rest]) + + defp mutate(:tag_value, [{k0, _v0} | rest], _bytes), do: map([{k0, <<0xC1, 0x00>>} | rest]) + + defp mutate(:simple_value, pairs, _bytes), + do: map(List.update_at(pairs, -1, fn {k, _} -> {k, <<0xF7>>} end)) + + defp mutate(:text_key, pairs, _bytes), do: map([{<<0x61, 0x61>>, <<0x00>>} | pairs]) + defp mutate(:negative_key, pairs, _bytes), do: map(pairs ++ [{<<0x20>>, <<0x00>>}]) + + defp mutate(:key_over_u32, pairs, _bytes), + do: map(pairs ++ [{<<0x1B, 0x1_0000_0000::64>>, <<0x00>>}]) + + defp mutate(:swap_keys, [a, b | rest], _bytes), do: map([b, a | rest]) + defp mutate(:swap_keys, _pairs, _bytes), do: nil + defp mutate(:duplicate_key, [a | rest], _bytes), do: map([a, a | rest]) + defp mutate(:trailing_bytes, _pairs, bytes), do: bytes <> <<0x00>> + defp mutate(:truncated, _pairs, bytes), do: binary_part(bytes, 0, byte_size(bytes) - 1) + + defp mutate(:not_nfc, pairs, _bytes), + do: map(pairs ++ [{<<0x1A, 0xFFFF_FFFE::32>>, <<0x63, 0x65, 0xCC, 0x81>>}]) + + defp map(pairs), do: <<0xA0 + length(pairs)>> <> join(pairs) + defp join(pairs), do: IO.iodata_to_binary(for {k, v} <- pairs, do: [k, v]) +end diff --git a/test/trinity/n6/cose_signing_test.exs b/test/trinity/n6/cose_signing_test.exs new file mode 100644 index 0000000..5dc0efc --- /dev/null +++ b/test/trinity/n6/cose_signing_test.exs @@ -0,0 +1,102 @@ +# SPDX-FileCopyrightText: Sudo Apt Holdings LLC +# SPDX-License-Identifier: Apache-2.0 +defmodule Trinity.N6.COSESigningTest do + @moduledoc """ + Slice 138, AC8: ML-DSA-87 signing and verification of a Sign1, where the runtime signs. Tagged + `:mldsa87_runtime` (slice 137), so it runs in the `fips` job's mode-off step and on a local + OpenSSL 3.5 runtime, and is excluded, by the printed decision, everywhere else. + + The key is NIST ACVP's published keyGen case (slice 137 pins it), so no key of this tree's is + published: its 32-byte seed and its expanded secret key are two forms of one key. + """ + + use ExUnit.Case, async: true + + alias Trinity.N6.COSE + alias Trinity.TestSupport.N6Vectors + + @moduletag :mldsa87_runtime + + @typ "application/vnd.example.test.v1+cose" + @typ_scitt "application/vnd.example.scitt-test.v1+cose" + + setup_all do + key = N6Vectors.acvp_key() + kid = COSE.thumbprint(key.public) + keys = fn k -> if k == kid, do: {:ok, key.public}, else: :error end + %{key: key, opts: [keys: keys, typ: N6Vectors.typ_table()]} + end + + test "signed with the seed and with the expanded key, both verify", %{key: key, opts: opts} do + for private <- [{:seed, key.seed}, key.expanded] do + assert {:ok, parts} = + COSE.sign("a statement", @typ, %{public: key.public, private: private}) + + assert byte_size(parts.signature) == 4627 + assert {:ok, %{exit: 0, verdict_code: :claim, typ: @typ}} = COSE.verify(parts, opts) + end + end + + test "one flipped byte in the protected header, the payload or the signature is invalid", %{ + key: key, + opts: opts + } do + {:ok, parts} = + COSE.sign("a statement", @typ, %{public: key.public, private: {:seed, key.seed}}) + + # In the protected header: the map head, the alg value (byte 3 of a3 01 38 31) and the typ's + # last byte. A byte inside the kid names another key, which is no trust, tested below. + positions = fn + :protected, bytes -> [0, 3, byte_size(bytes) - 1] + _field, bytes -> [0, div(byte_size(bytes), 2), byte_size(bytes) - 1] + end + + for field <- [:protected, :payload, :signature], + at <- positions.(field, Map.fetch!(parts, field)) do + bytes = Map.fetch!(parts, field) + <> = bytes + flipped = Map.put(parts, field, <>) + + assert {:error, 1, _code} = COSE.verify(flipped, opts), "#{field} byte #{at}" + end + end + + test "both exchange forms carry the same parts, and each verifies", %{key: key, opts: opts} do + {:ok, parts} = + COSE.sign("a statement", @typ, %{public: key.public, private: {:seed, key.seed}}) + + assert {:ok, detached} = COSE.parse(COSE.render(parts, :detached), :detached, parts.payload) + assert {:ok, attached} = COSE.parse(COSE.render(parts, :attached_tagged), :attached_tagged) + assert detached == parts and attached == parts + assert {:ok, _} = COSE.verify(detached, opts) + assert {:ok, _} = COSE.verify(attached, opts) + end + + test "a SCITT-renderable typ carries the CWT claims, protected", %{key: key, opts: opts} do + {:ok, parts} = + COSE.sign("a statement", @typ_scitt, %{public: key.public, private: {:seed, key.seed}}, + cwt: {"iss.invalid", "sub"} + ) + + assert {:ok, %{exit: 0}} = COSE.verify(parts, opts) + + assert {:ok, %{cwt: %{iss: "iss.invalid", sub: "sub"}}} = + COSE.decode_protected(parts.protected, opts[:typ]) + end + + test "a kid nobody registered is no trust, including one flipped inside a signed header", %{ + key: key, + opts: opts + } do + {:ok, parts} = + COSE.sign("a statement", @typ, %{public: key.public, private: {:seed, key.seed}}) + + assert COSE.verify(parts, Keyword.put(opts, :keys, fn _ -> :error end)) == + {:error, 5, :policy_stale_or_unknown_key} + + # The kid's bytes start after a3 01 38 31 04 58 30. + <> = parts.protected + flipped = %{parts | protected: <>} + assert COSE.verify(flipped, opts) == {:error, 5, :policy_stale_or_unknown_key} + end +end diff --git a/test/trinity/n6/cose_test.exs b/test/trinity/n6/cose_test.exs new file mode 100644 index 0000000..f991c92 --- /dev/null +++ b/test/trinity/n6/cose_test.exs @@ -0,0 +1,168 @@ +# SPDX-FileCopyrightText: Sudo Apt Holdings LLC +# SPDX-License-Identifier: Apache-2.0 +defmodule Trinity.N6.COSETest do + @moduledoc """ + Slice 138, AC6 and AC7, and the parts of AC8 that need no signature: the key thumbprint, the + header rules, the unprotected header, ToBeSigned, and what `verify/2` answers when it cannot + judge. None of it needs a runtime that signs ML-DSA-87, so it runs on every leg. + """ + + use ExUnit.Case, async: true + + alias Trinity.N6.{CBOR, COSE, Profile} + alias Trinity.TestSupport.{MLDSA87Oracle, N6Vectors} + + @typ "application/vnd.example.test.v1+cose" + + setup_all do + key = N6Vectors.acvp_key() + %{key: key, kid: COSE.thumbprint(key.public), table: N6Vectors.typ_table()} + end + + describe "AC6: the thumbprint" do + test "kid for ACVP's published key is SHA-384 over exactly the RFC 9679 input", %{key: key} do + pub = File.read!(Path.join(N6Vectors.root(), "thumbprint/acvp_mldsa87_tg3_tc51.pub")) + kid = File.read!(Path.join(N6Vectors.root(), "thumbprint/acvp_mldsa87_tg3_tc51.kid")) + assert pub == key.public + + input = <<0xA3, 0x01, 0x07, 0x03, 0x38, 0x31, 0x20, 0x59, 0x0A, 0x20>> <> pub + assert COSE.thumbprint(pub) == kid + assert :crypto.hash(:sha384, input) == kid + + # The same input is the RFC 8949 section 4.2.1 encoding of {1: 7, 3: -50, -1: pub}, but + # N6 cannot build it (key -1 is outside its keys), which is why the prefix is fixed by hand. + assert CBOR.encode(%{1 => 7, 3 => -50}) == {:ok, <<0xA2, 0x01, 0x07, 0x03, 0x38, 0x31>>} + end + end + + describe "AC7: the header rules" do + test "every positive header decodes, and the builder makes exactly the plain one", %{ + kid: kid, + table: table + } do + for path <- Path.wildcard(Path.join(N6Vectors.root(), "header/positive/*.cbor")) do + assert {:ok, %{kid: ^kid}} = COSE.decode_protected(File.read!(path), table), path + end + + plain = File.read!(Path.join(N6Vectors.root(), "header/positive/alg_kid_typ.cbor")) + assert COSE.protected_header(kid, @typ) == plain + end + + test "every negative header is refused with its code", %{table: table} do + vectors = N6Vectors.negative("header") + assert length(vectors) >= 17 + + for {defect, id, bytes, code} <- vectors do + assert COSE.decode_protected(bytes, table) == {:error, code}, "#{defect}/#{id}" + assert Profile.exit_of(code) == 1 + end + end + + test "the unprotected header must be empty", %{kid: kid} do + p = COSE.protected_header(kid, @typ) + sig = :binary.copy(<<0>>, 4627) + parts = %{protected: p, payload: "x", signature: sig} + + assert {:ok, ^parts} = COSE.parse(COSE.render(parts, :detached), :detached, "x") + assert {:ok, ^parts} = COSE.parse(COSE.render(parts, :attached_tagged), :attached_tagged) + + sign1 = fn unprotected, payload -> + CBOR.encode!([{:bytes, p}, unprotected, payload, {:bytes, sig}]) + end + + for label <- [7, 9, 11, 12] do + assert COSE.parse(sign1.(%{label => {:bytes, "c"}}, nil), :detached, "x") == + {:error, :countersignature_forbidden} + end + + for label <- [1, 4, 16] do + assert COSE.parse(sign1.(%{label => 0}, nil), :detached, "x") == + {:error, :header_not_protected} + end + + assert COSE.parse(sign1.(%{5 => 0}, nil), :detached, "x") == + {:error, :unprotected_forbidden} + + # A negative label is refused by the codec's key rule; the only map in a Sign1 is this one. + negative = + <<0x84, 0x58, byte_size(p)>> <> p <> <<0xA1, 0x20, 0x00, 0xF6, 0x59, 4627::16>> <> sig + + assert COSE.parse(negative, :detached, "x") == {:error, :unprotected_forbidden} + + assert COSE.parse(sign1.(%{}, {:bytes, "x"}), :detached, "x") == + {:error, :carrier_malformed} + + assert COSE.parse(sign1.(%{}, nil), :attached_tagged) == {:error, :carrier_malformed} + + assert COSE.parse(<<0xD2>> <> sign1.(%{}, nil), :attached_tagged) == + {:error, :carrier_malformed} + end + end + + describe "AC8, without signing" do + test "ToBeSigned is RFC 9052's Sig_structure with an empty external_aad", %{kid: kid} do + p = COSE.protected_header(kid, @typ) + + assert COSE.to_be_signed(p, "payload") == + <<0x84, 0x6A, "Signature1", 0x58, byte_size(p)>> <> p <> <<0x40, 0x47, "payload">> + end + + test "an unknown kid is no trust (5), before any signature is checked", %{ + kid: kid, + table: table + } do + parts = %{ + protected: COSE.protected_header(kid, @typ), + payload: "x", + signature: :binary.copy(<<0>>, 4627) + } + + assert COSE.verify(parts, keys: fn _ -> :error end, typ: table) == + {:error, 5, :policy_stale_or_unknown_key} + end + + test "a registered key whose thumbprint is not the kid is refused", %{key: key, table: table} do + other_kid = :crypto.hash(:sha384, "not this key") + + parts = %{ + protected: COSE.protected_header(other_kid, @typ), + payload: "x", + signature: :binary.copy(<<0>>, 4627) + } + + assert COSE.verify(parts, keys: fn _ -> {:ok, key.public} end, typ: table) == + {:error, 1, :kid_not_thumbprint} + end + + test "a signature of the wrong size is a malformed carrier", %{ + key: key, + kid: kid, + table: table + } do + parts = %{protected: COSE.protected_header(kid, @typ), payload: "x", signature: <<0>>} + + assert COSE.verify(parts, keys: fn _ -> {:ok, key.public} end, typ: table) == + {:error, 1, :carrier_malformed} + end + + test "a runtime without ML-DSA-87 answers no trust, never a crash; one with it judges the signature", + %{key: key, kid: kid, table: table} do + parts = %{ + protected: COSE.protected_header(kid, @typ), + payload: "x", + signature: :binary.copy(<<0>>, 4627) + } + + answer = COSE.verify(parts, keys: fn _ -> {:ok, key.public} end, typ: table) + + if MLDSA87Oracle.signs?() do + assert answer == {:error, 1, :signature_invalid} + else + assert answer == {:error, 5, :alg_unavailable} + + assert COSE.sign("x", @typ, %{public: key.public, private: {:seed, key.seed}}) == + {:error, :alg_unavailable} + end + end + end +end diff --git a/test/trinity/n6/vectors_test.exs b/test/trinity/n6/vectors_test.exs new file mode 100644 index 0000000..8c11c1d --- /dev/null +++ b/test/trinity/n6/vectors_test.exs @@ -0,0 +1,54 @@ +# SPDX-FileCopyrightText: Sudo Apt Holdings LLC +# SPDX-License-Identifier: Apache-2.0 +defmodule Trinity.N6.VectorsTest do + @moduledoc """ + Slice 138: the checked-in vector set is exactly what `scripts/n6_vectors.exs` writes, and its + `SHA384SUMS` lists every file, so the set a reader is offered is the set the suite tests. + """ + + use ExUnit.Case, async: true + + alias Trinity.TestSupport.N6Vectors + + test "SHA384SUMS lists every file of the set, with its digest" do + root = N6Vectors.root() + + listed = + Path.join(root, "SHA384SUMS") + |> File.read!() + |> String.split("\n", trim: true) + |> Map.new(fn line -> + [hex, path] = String.split(line, " ", parts: 2) + {path, hex} + end) + + files = + Path.join(root, "**") + |> Path.wildcard(match_dot: true) + |> Enum.filter(&File.regular?/1) + |> Enum.map(&Path.relative_to(&1, root)) + |> Enum.reject(&(&1 == "SHA384SUMS")) + + assert Enum.sort(Map.keys(listed)) == Enum.sort(files) + + for path <- files do + digest = + :crypto.hash(:sha384, File.read!(Path.join(root, path))) |> Base.encode16(case: :lower) + + assert listed[path] == digest, path + end + end + + @tag :tmp_dir + test "the generator writes the checked-in set byte for byte", %{tmp_dir: tmp} do + out = Path.join(tmp, "n6") + + {output, status} = + System.cmd("elixir", ["scripts/n6_vectors.exs", out], stderr_to_stdout: true) + + assert status == 0, output + + assert File.read!(Path.join(out, "SHA384SUMS")) == + File.read!(Path.join(N6Vectors.root(), "SHA384SUMS")) + end +end From 45930dffbceb2d2afd7544d2421775cdb810efc5 Mon Sep 17 00:00:00 2001 From: Ayla Croft Date: Mon, 19 Oct 2026 20:19:37 -0400 Subject: [PATCH 2/2] fix(s138): verify answers no trust when FIPS mode's provider refuses ML-DSA-87 with :error, never a crash Signed-off-by: Ayla Croft --- lib/trinity/n6/cose.ex | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/lib/trinity/n6/cose.ex b/lib/trinity/n6/cose.ex index 543fdec..7e6693f 100644 --- a/lib/trinity/n6/cose.ex +++ b/lib/trinity/n6/cose.ex @@ -223,10 +223,14 @@ defmodule Trinity.N6.COSE do e in ErlangError -> unavailable(e) end - # :crypto raises :notsup in FIPS mode on a provider that refuses ML-DSA-87, and badarg where - # the linked library has no ML-DSA at all. Either way this runtime cannot judge. - defp unavailable(%ErlangError{original: {reason, _, _}}) when reason in [:notsup, :badarg], - do: {:error, :alg_unavailable} + # How :crypto says the runtime cannot do ML-DSA-87, each measured: `badarg` ("Bad key type") + # where the linked OpenSSL has no ML-DSA (3.0); `error` ("Can't create PKEY_CTX from id") from + # `verify` in FIPS mode on Red Hat's provider (the `fips` job, PR #133); `notsup` ("Unsupported + # algorithm in FIPS mode") for an algorithm the mode refuses. Key and signature sizes are checked + # before any call, so these are the runtime's answer, not the caller's mistake: no trust. + defp unavailable(%ErlangError{original: {reason, _, _}}) + when reason in [:notsup, :badarg, :error], + do: {:error, :alg_unavailable} defp unavailable(e), do: raise(e)