[Cbor] Flattening in cddlc 0.1.7

Carsten Bormann <cabo@tzi.org> Sat, 11 March 2023 16:58 UTC

Return-Path: <cabo@tzi.org>
X-Original-To: cbor@ietfa.amsl.com
Delivered-To: cbor@ietfa.amsl.com
Received: from localhost (localhost [127.0.0.1]) by ietfa.amsl.com (Postfix) with ESMTP id 3B6D1C151553 for <cbor@ietfa.amsl.com>; Sat, 11 Mar 2023 08:58:01 -0800 (PST)
X-Virus-Scanned: amavisd-new at amsl.com
X-Spam-Flag: NO
X-Spam-Score: -4.186
X-Spam-Level:
X-Spam-Status: No, score=-4.186 tagged_above=-999 required=5 tests=[BAYES_00=-1.9, RCVD_IN_DNSWL_MED=-2.3, RCVD_IN_ZEN_BLOCKED_OPENDNS=0.001, SPF_HELO_NONE=0.001, T_SPF_TEMPERROR=0.01, URIBL_DBL_BLOCKED_OPENDNS=0.001, URIBL_ZEN_BLOCKED_OPENDNS=0.001] autolearn=ham autolearn_force=no
Received: from mail.ietf.org ([50.223.129.194]) by localhost (ietfa.amsl.com [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id c9KysYp34dji for <cbor@ietfa.amsl.com>; Sat, 11 Mar 2023 08:57:55 -0800 (PST)
Received: from smtp.uni-bremen.de (mail.uni-bremen.de [134.102.50.15]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by ietfa.amsl.com (Postfix) with ESMTPS id CED68C151527 for <cbor@ietf.org>; Sat, 11 Mar 2023 08:57:52 -0800 (PST)
Received: from [192.168.217.124] (p548dc9a4.dip0.t-ipconnect.de [84.141.201.164]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.uni-bremen.de (Postfix) with ESMTPSA id 4PYpy52Yy7zDCbN; Sat, 11 Mar 2023 17:57:49 +0100 (CET)
From: Carsten Bormann <cabo@tzi.org>
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: quoted-printable
X-Mao-Original-Outgoing-Id: 700246668.926782-fcc5dd2571ba44962a637c8aab159a89
Mime-Version: 1.0 (Mac OS X Mail 13.4 \(3608.120.23.2.7\))
Date: Sat, 11 Mar 2023 17:57:48 +0100
Message-Id: <B4666F20-A99D-483C-81C9-C15A790E5C57@tzi.org>
To: cbor@ietf.org
X-Mailer: Apple Mail (2.3608.120.23.2.7)
Archived-At: <https://mailarchive.ietf.org/arch/msg/cbor/n3ai0dhQ2XlQ1-j4oDxnH0HQOHw>
Subject: [Cbor] Flattening in cddlc 0.1.7
X-BeenThere: cbor@ietf.org
X-Mailman-Version: 2.1.39
Precedence: list
List-Id: "Concise Binary Object Representation \(CBOR\)" <cbor.ietf.org>
List-Unsubscribe: <https://www.ietf.org/mailman/options/cbor>, <mailto:cbor-request@ietf.org?subject=unsubscribe>
List-Archive: <https://mailarchive.ietf.org/arch/browse/cbor/>
List-Post: <mailto:cbor@ietf.org>
List-Help: <mailto:cbor-request@ietf.org?subject=help>
List-Subscribe: <https://www.ietf.org/mailman/listinfo/cbor>, <mailto:cbor-request@ietf.org?subject=subscribe>
X-List-Received-Date: Sat, 11 Mar 2023 16:58:01 -0000

Based on some input at the CDDL-2 design team meeting last week, I prototyped my impression of what “flattening” might be.  Now in cddlc 0.1.7 (as usual, `gem update`).  We don’t need to “standardize” this functionality for interchange, but it might be on a level with Section 6 of cddl-freezer — things we should agree to enable interchange between tools.

[1]: https://www.ietf.org/archive/id/draft-bormann-cbor-cddl-freezer-10.html#name-alternative-representations

Here is an input (slightly filled up with test cases):

COSE_KDF_Context = [
    AlgorithmID : int / tstr,
    PartyUInfo : [ PartyInfo ],
    PartyVInfo : [ PartyInfo ],
    SuppPubInfo : [
        keyDataLength : uint,
        protected : empty_or_serialized_map,
        ? other : bstr
        namecollision: 4712
        &(flag1: 1) => text
        &(flag2: 2) => text
        3: three
    ],
    ? SuppPrivInfo : bstr
    namecollision: 4711
    &(flag1: 3) => text
]

And here is the output from `cddlc -f2rtcddl -`:

COSE_KDF_Context = [
  "AlgorithmID" => $.AlgorithmID,
  "PartyUInfo" => $.PartyUInfo,
  "PartyVInfo" => $.PartyVInfo,
  "SuppPubInfo" => $.SuppPubInfo,
  ? "SuppPrivInfo" => $.SuppPrivInfo,
  "namecollision" => $.COSE_KDF_Context$namecollision,
  &("flag1" => 3) => $.COSE_KDF_Context$flag1,
]
$.AlgorithmID = int / tstr
$.PartyUInfo = [PartyInfo]
$.PartyVInfo = [PartyInfo]
$.keyDataLength = uint
$.protected = empty_or_serialized_map
$.other = bstr
$.$.SuppPubInfo$namecollision = 4712
$.$.SuppPubInfo$flag1 = text
$.$.SuppPubInfo$flag2 = text
$.$.SuppPubInfo$3 = three
$.SuppPubInfo = [
  "keyDataLength" => $.keyDataLength,
  "protected" => $.protected,
  ? "other" => $.other,
  "namecollision" => $.$.SuppPubInfo$namecollision,
  &("flag1" => 1) => $.$.SuppPubInfo$flag1,
  &("flag2" => 2) => $.$.SuppPubInfo$flag2,
  3 => $.$.SuppPubInfo$3,
]
$.SuppPrivInfo = bstr
$.COSE_KDF_Context$namecollision = 4711
$.COSE_KDF_Context$flag1 = text

This is clearly not meant for humans.
Code generators might want to use this view as it essentially defines a separate rule for every use of a group entry in a map or array that has something that looks like a name.

Comments welcome.
It is entertaining to watch this code working on rfc9052.cddl.  Below…

Grüße, Carsten


start = COSE_Messages / COSE_Key / COSE_KeySet / Internal_Types
Internal_Types = Sig_structure / Enc_structure / MAC_structure
label = int / tstr
values = any
COSE_Messages = COSE_Untagged_Message / COSE_Tagged_Message
COSE_Untagged_Message = COSE_Sign / COSE_Sign1 / COSE_Encrypt / COSE_Encrypt0 / COSE_Mac / COSE_Mac0
COSE_Tagged_Message = COSE_Sign_Tagged / COSE_Sign1_Tagged / COSE_Encrypt_Tagged / COSE_Encrypt0_Tagged / COSE_Mac_Tagged / COSE_Mac0_Tagged
Headers = (
  "protected" => $.protected,
  "unprotected" => $.unprotected,
  )
header_map = {
  Generic_Headers,
  * label => values,
}
empty_or_serialized_map = bstr .cbor header_map / bstr .size 0
Generic_Headers = (
  ? 1 => $.Generic_Headers$1,
  ? 2 => $.Generic_Headers$2,
  ? 3 => $.Generic_Headers$3,
  ? 4 => $.Generic_Headers$4,
  ? (5 => $.Generic_Headers$5 // 6 => $.Generic_Headers$6),
  )
COSE_Sign_Tagged = #6.98(COSE_Sign)
COSE_Sign = [
  Headers,
  "payload" => $.COSE_Sign$payload,
  "signatures" => $.signatures,
]
COSE_Signature = [
  Headers,
  "signature" => $.signature,
]
COSE_Sign1_Tagged = #6.18(COSE_Sign1)
COSE_Sign1 = [
  Headers,
  "payload" => $.COSE_Sign1$payload,
  "signature" => $.signature,
]
Sig_structure = [
  "context" => $.Sig_structure$context,
  "body_protected" => $.body_protected,
  ? "sign_protected" => $.sign_protected,
  "external_aad" => $.external_aad,
  "payload" => $.Sig_structure$payload,
]
COSE_Encrypt_Tagged = #6.96(COSE_Encrypt)
COSE_Encrypt = [
  Headers,
  "ciphertext" => $.ciphertext,
  "recipients" => $.recipients,
]
COSE_recipient = [
  Headers,
  "ciphertext" => $.ciphertext,
  ? "recipients" => $.recipients,
]
COSE_Encrypt0_Tagged = #6.16(COSE_Encrypt0)
COSE_Encrypt0 = [
  Headers,
  "ciphertext" => $.ciphertext,
]
Enc_structure = [
  "context" => $.Enc_structure$context,
  "protected" => $.protected,
  "external_aad" => $.external_aad,
]
COSE_Mac_Tagged = #6.97(COSE_Mac)
COSE_Mac = [
  Headers,
  "payload" => $.COSE_Mac$payload,
  "tag" => $.tag,
  "recipients" => $.recipients,
]
COSE_Mac0_Tagged = #6.17(COSE_Mac0)
COSE_Mac0 = [
  Headers,
  "payload" => $.COSE_Mac0$payload,
  "tag" => $.tag,
]
MAC_structure = [
  "context" => $.MAC_structure$context,
  "protected" => $.protected,
  "external_aad" => $.external_aad,
  "payload" => $.MAC_structure$payload,
]
COSE_Key = {
  1 => $.COSE_Key$1,
  ? 2 => $.COSE_Key$2,
  ? 3 => $.COSE_Key$3,
  ? 4 => $.COSE_Key$4,
  ? 5 => $.COSE_Key$5,
  * label => values,
}
COSE_KeySet = [+ COSE_Key]
$.protected = empty_or_serialized_map
$.unprotected = header_map
$.Generic_Headers$1 = int / tstr
$.Generic_Headers$2 = [+ label]
$.Generic_Headers$3 = tstr / int
$.Generic_Headers$4 = bstr
$.Generic_Headers$5 = bstr
$.Generic_Headers$6 = bstr
$.COSE_Sign$payload = bstr / nil
$.signatures = [+ COSE_Signature]
$.signature = bstr
$.COSE_Sign1$payload = bstr / nil
$.Sig_structure$context = "Signature" / "Signature1"
$.body_protected = empty_or_serialized_map
$.sign_protected = empty_or_serialized_map
$.external_aad = bstr
$.Sig_structure$payload = bstr
$.ciphertext = bstr / nil
$.recipients = [+ COSE_recipient]
$.Enc_structure$context = "Encrypt" / "Encrypt0" / "Enc_Recipient" / "Mac_Recipient" / "Rec_Recipient"
$.COSE_Mac$payload = bstr / nil
$.tag = bstr
$.COSE_Mac0$payload = bstr / nil
$.MAC_structure$context = "MAC" / "MAC0"
$.MAC_structure$payload = bstr
$.COSE_Key$1 = tstr / int
$.COSE_Key$2 = bstr
$.COSE_Key$3 = tstr / int
$.COSE_Key$4 = [+ tstr / int]
$.COSE_Key$5 = bstr