メインコンテンツまでスキップ

3. CBOR ベースのプロトコルの作成

CBOR のようなデータ形式は、形式のネゴシエーションが存在しない環境で使われることがよくあります。CBOR の特定の設計目標は、含まれる、あるいは想定されるスキーマを必要としないことです。デコーダーは CBOR 項目を受け取り、他に知識がなくてもそれをデコードできます。

もちろん、現実の実装では、エンコーダーとデコーダーは CBOR データ項目に何が含まれるべきかについて共通の見解を持ちます。例えば、合意された形式は「その項目は配列であり、最初の値は UTF-8 文字列、2 番目の値は整数、後続の値は 0 個以上の浮動小数点数である」や「その項目はキーとしてバイト文字列を持ち、キーが 0xab01 であるペアを少なくとも 1 つ含むマップである」といったものかもしれません。

本仕様は CBOR ベースのプロトコルに制限を課しません。エンコーダーは、それが使われるプロトコルで要求されるだけの、多くの、あるいは少ない種類の値をエンコードできます。デコーダーは、それが使われるプロトコルで要求されるだけの、多くの、あるいは少ない種類の値を理解できます。この制限のなさにより、CBOR は極めて制約の厳しい環境でも使用できます。

このセクションでは、CBOR ベースのプロトコルを作成する際の考慮事項をいくつか説明します。これは助言のみであり、RFC 2119 の意味で "MAY" と解釈できる語を除き、RFC 2119 のいかなる表現も明示的に除外します。

3.1. ストリーミングアプリケーションにおける CBOR​

ストリーミングアプリケーションでは、データストリームは連続して連結された一連の CBOR データ項目で構成される場合があります。そのような環境では、直前のデータ項目の終了後にデータが見つかった場合、デコーダーはただちに新しいデータ項目のデコードを開始します。

データ項目を構成するすべてのバイトがただちにデコーダーに利用できるとは限りません。完全なデータ項目がアプリケーションに提示できるようになるまで追加データをバッファリングするデコーダーもあります。他のデコーダーは、トップレベルのデータ項目に関する部分的な情報 (すでにデコード可能なネストされたデータ項目や、まだ完全には到着していないバイト文字列の一部など) をアプリケーションに提示できます。

不定長エンコーディングの使用を望まないアプリケーションやプロトコルもあることに注意してください。不定長エンコーディングを使用すると、エンコーダーはカウントのためにすべてのデータを整列させる必要がなくなりますが、デコーダーは項目の終了を待つ間に増大するメモリを割り当てる必要があります。これは一部のアプリケーションでは問題ありませんが、他のアプリケーションでは問題になります。

3.2. ジェネリックなエンコーダーとデコーダー​

ジェネリックな CBOR デコーダーは、すべての整形式の CBOR データをデコードしてアプリケーションに提示できます。CBOR データは、初期バイト、およびその値によって暗示されるバイト文字列やデータ項目を CBOR で定義された方法で使用し、後続する余分なデータがない場合に整形式です (付録 C)。

CBOR はこれらの事例を最小化しようとしていますが、整形式の CBOR データがすべて有効であるわけではありません。例えば、この形式は拡張バイトでエンコードされた 32 未満のシンプル値を除外します。また、特定のタグは、ビッグナムタグの中にタグを含めたり、日付タグの中でバイト文字列を後続させたりするなど、違反される可能性のある意味論的制約を課す場合があります。最後に、データが無効である場合もあります。無効な UTF-8 文字列や、[RFC3339] に準拠しない日付文字列などです。ジェネリックなエンコーダーおよびデコーダーが、無効なデータの処理を可能にするためにアプリケーションインターフェースとして不自然な選択をすることは要求されません。ジェネリックなエンコーダーおよびデコーダーは、その特定のコードポイントがエンコーダー/デコーダーの作成時に登録されていなくても、シンプル値やタグを転送することが期待されます (セクション 3.5)。

ジェネリックなデコーダーは、有効なものも無効なものも含め、整形式の CBOR 値をアプリケーションに提示する手段を提供します。診断記法 (セクション 6) は、整形式の CBOR 値を人間に提示するために使用できます。

ジェネリックなエンコーダーは、エンコーダーが知らないシンプル値やタグを含む、任意の整形式の値をアプリケーションが指定できるインターフェースを提供します。

3.3. 構文エラー​

整形式でない CBOR データ項目に遭遇したデコーダーは、一般に、デコードを完全に失敗させる (エラーを発行する、および/または処理を完全に停止する)、問題のあるデータおよびデータ項目を、問題が発生したことを明確に示すデコーダー固有の慣例に従って置き換える、または他の何らかのアクションを取る、という選択ができます。

3.3.1. 不完全な CBOR データ項目​

CBOR データ項目の表現は、その初期バイトと、そのデータ項目に含まれるデータ項目の構造によって決まる特定の長さを持ちます。利用できるデータが少ない場合、これは構文エラーとして扱えます。デコーダーはインクリメンタル解析、つまりデータ項目を利用可能な範囲までデコードし、そこまでに見つかったデータを (イベントベースのインターフェースなどで) 提示し、さらにデータが利用可能になった時点でデコードを継続するオプションを実装することもできます。

不完全なデータ項目の例には次のものがあります:

  • デコーダーが一定数の配列またはマップのエントリを期待していたのに、データの終端に遭遇する。

  • デコーダーがマップの最後のペアであると期待するものを処理し、データの終端に到達する。

  • デコーダーがタグを見た直後に、データの終端に遭遇する。

  • デコーダーが不定長項目の開始を見たが、"break" 停止コードを見る前にデータの終端に遭遇する。

3.3.2. 不正な不定長項目​

不正な不定長データ項目の例には次のものがあります:

  • 不定長のバイト文字列またはテキスト内で、デコーダーが "break" 停止コードを見つける前に、適切な主タイプでない項目を見つける。

  • 不定長マップ内で、デコーダーがキーを読み取った直後に "break" 停止コードに遭遇する (値が欠落している)。

もう 1 つのエラーは、直ちに囲んでいる (閉じられていない) 不定長項目が存在しないデータ上の位置で "break" 停止コードを見つけることです。

3.3.3. 未知の追加情報値​

執筆時点では、一部の追加情報値は未割り当てであり、本文書の将来のバージョンのために予約されています (セクション 5.2 を参照)。これらの追加情報値の全体的な構文はまだ定義されていないため、理解できない追加情報値を見たデコーダーは解析を継続できません。

3.4. その他のデコードエラー​

CBOR データ項目は構文的には整形式であっても、CBOR データモデルでエンコードされたデータを解釈する上で問題を呈する場合があります。一般に、そのような問題を持つデータ項目を見つけたデコーダーは、警告を発行する、処理を完全に停止する、エラーを処理して問題のある値をそのままアプリケーションに利用可能にする、または他の何らかの種類のアクションを取る可能性があります。

そのような問題には次のものが含まれます:

マップ内の重複キー: ジェネリックなデコーダー (セクション 3.2) は、ネイティブな CBOR データモデルを使用してアプリケーションにデータを提供します。このデータモデルにはマップ (一意なキーを持つキーと値の対応) が含まれ、マルチマップ (複数のエントリが同じキーを持てるキーと値の対応) は含まれません。したがって、重複キーを持つ CBOR マップ項目を受け取ったジェネリックなデコーダーは、そのキーの 1 つのインスタンスのみを持つマップにデコードするか、処理を完全に停止する可能性があります。一方、「ストリーミングデコーダー」はそれにさえ気づかない可能性があります (セクション 3.7)。

タグに後続する値の許容されない型: タグ (セクション 2.4) は、タグに後続すべきデータ項目の型を指定します。例えば、正または負のビッグナムのタグはバイト文字列に付けることになっています。タグ付きデータ項目をネイティブな表現 (この例ではネイティブな多倍長整数) にデコードするデコーダーは、タグ付けされるデータ項目の型をチェックすることが期待されます。自身の環境でそのようなネイティブな表現が利用できないデコーダーであっても、既知のタグについてはチェックを行い、適切に対応できます。

無効な UTF-8 文字列: デコーダーは、UTF-8 文字列 (主タイプ 3) のバイト列が実際に有効な UTF-8 であることを検証し、適切に対応することを望む場合もあれば、望まない場合もあります。

3.5. 未知のシンプル値とタグの扱い​

認識しないシンプル値 (セクション 2.3) に遭遇したデコーダー、例えばデコーダーの配備後に IANA レジストリに追加された値や、デコーダーが実装しないことを選んだ値などに対しては、警告を発行する、処理を完全に停止する、未知の値をそのままアプリケーションに利用可能にすることでエラーを処理する (ジェネリックなデコーダーに期待されるように)、または他の何らかの種類のアクションを取る可能性があります。

認識しないタグ (セクション 2.4) に遭遇したデコーダー、例えばデコーダーの配備後に IANA レジストリに追加されたタグや、デコーダーが実装しないことを選んだタグなどに対しては、警告を発行する、処理を完全に停止する、エラーを処理して未知のタグ値を含まれるデータ項目とともにアプリケーションに提示する (ジェネリックなデコーダーに期待されるように)、タグを無視して含まれるデータ項目のみをアプリケーションに提示する、または他の何らかの種類のアクションを取る可能性があります。

3.6. 数値​

本仕様の目的上、同じ数値に対するすべての数値表現は等価です。これは、エンコーダーが浮動小数点値 0.0 を整数 0 としてエンコードできることを意味します。しかしそれはまた、整数値のみを期待するアプリケーションが、エンコーダーがそれらが望ましいと判断した場合 (浮動小数点値のほうが 64 ビット整数よりコンパクトな場合など) に浮動小数点値を見つける可能性があることも意味します。

CBOR を使用するアプリケーションやプロトコルは、数値の表現を制限する場合があります。例えば、整数のみを扱うプロトコルは、浮動小数点数を使用してはならず、そのプロトコルのデコーダーは浮動小数点数を扱える必要がないと定めるかもしれません。同様に、CBOR を使用するプロトコルやアプリケーションは、デコーダーがいずれの型の数値も扱える必要があると定めるかもしれません。

CBOR ベースのプロトコルは、言語環境によって表現可能な数値の範囲と精度に関する制約が異なることを考慮すべきです。例えば、JavaScript の数値システムはすべての数値を浮動小数点として扱うため、53 ビットを超える有効桁を持つ整数のデコードで暗黙のうちに精度が失われる可能性があります。数値を使用するプロトコルは、デコーダーや受信アプリケーションにおける非自明な数値の扱いに関する期待を定義すべきです。

浮動小数点数を含む CBOR ベースのプロトコルは、3 つの形式 (半精度、単精度、倍精度) のうちどれをサポートすべきかを制限できます。整数のみのアプリケーションでは、プロトコルは浮動小数点値の使用を完全に除外することを望む場合があります。

コンパクトさを重視して設計された CBOR ベースのプロトコルは、アプリケーションにとって必要以上に長い特定の整数エンコーディングを除外することを望む場合があります。例えば 64 ビット整数を実装する必要性をなくすためなどです。エンコーダーは、与えられた値を表現できる最もコンパクトな整数表現を使用することが期待されます。しかし、コンパクトなアプリケーションは、アプリケーションがそのサイズの整数をデコードできる限り、必要以上に長いエンコーディングを使用する値 (例えば "0" を 0b000_11101 に続けて 2 バイトの 0x00 としてエンコードする) を受け入れるべきです。

3.7. マップのキーの指定​

エンコードおよびデコードするアプリケーションは、マップで使用するキーの型について合意する必要があります。JSON ベースのアプリケーションと相互運用する必要があるアプリケーションでは、キーはおそらく UTF-8 文字列のみに限定されるべきです。そうしない場合、他の CBOR 型から Unicode 文字への指定された対応付けが必要になり、これはしばしば実装エラーにつながります。キーが本質的に数値であり、キーの数値順がアプリケーションにとって重要である場合、数値を直接キーとして使用することが有用です。

複数の型のキーを使用する場合、それらの型が使用される特定のプログラミング環境でどのように表現されるかを考慮すべきです。例えば JavaScript のオブジェクトでは、整数 1 のキーと文字列 "1" のキーを区別できません。これは、整数キーを使用する場合、数字のように見える文字列キーの同時使用を避ける必要があることを意味します。これも、キーは単一の CBOR 型にすべきであるという結論につながります。

CBOR データ項目内にネストされたデータ項目をデコードした直ちに配信するデコーダー ("ストリーミングデコーダー") は、多くの場合、マップ内のキーの一意性を確認するために必要な状態を保持しません。同様に、囲むデータ項目が完全に利用可能になる前にデータ項目のエンコードを開始できるエンコーダー ("ストリーミングエンコーダー") は、データソースが一意性を維持することに依存することで、オーバーヘッドを大幅に削減したい場合があります。

CBOR ベースのプロトコルは、受信アプリケーションがマップ内に複数の同一キーを見たときに何をすべきかについて、意図的な決定を下すべきです。プロトコル内の結果として得られる規則は CBOR データモデルを尊重すべきです。それは、マップ内に同一キーがあることが不正なマップを示し、デコーダーはエラーで停止しなければならないという規則を持つ場合を除き、同一キーを持つエントリの特定の扱いを規定できません。重複キーは、strict モードを使用する CBOR デコーダーでも禁止されています (セクション 3.10)。

マップのための CBOR データモデルは、マップ表現におけるキー/値ペアの順序に意味を帰することを許しません。したがって、マップ内のキー/値ペアの順序を変更すると意味が変わるような CBOR ベースのプロトコルを定義することは、些細な側面 (キャッシュの使用など) を除けば、非常に悪い習慣です。(CBOR ベースのプロトコルは、正規化のためなどに、特定のシリアライゼーション順序を規定できます。)

頻繁に使用されるキーが 24 個以下のマップを持つ制約のあるデバイス向けアプリケーションは、小さな整数の使用を検討すべきです (頻繁に使用されるキーが 48 個までのものは、小さな負の整数の使用も検討すべきです)。それらのキーを 1 バイトでエンコードできるからです。

3.8. 未定義値​

一部の CBOR ベースのプロトコルでは、シンプル値 (セクション 2.3) である Undefined が、囲んでいる残りのデータ項目を害なくエンコードできるようにするため、エンコードに問題のあるデータ項目の代用品としてエンコーダーによって使用される場合があります。

3.9. 正規 CBOR​

一部のプロトコルは、エンコーダーが特定の正規形式でのみ CBOR を出力することを望む場合があります。それらのプロトコルは、デコーダーにその入力が正規であることを検査させる場合もあります。それらのプロトコルは、正規形式によって何を意味するか、およびエンコーダーとデコーダーに何が期待されるかを自由に定義できます。このセクションは、そのようなプロトコルのためのいくつかの提案を示します。

プロトコルが「正規」を、同じ入力データから開始する 2 つのエンコーダー実装が同じ CBOR 出力を生成するという意味だと考える場合、次の 4 つの規則で十分です:

  • 整数は可能な限り小さくなければなりません。

    • 0 から 23 および -1 から -24 は、主タイプと同じバイトで表現されなければなりません。

    • 24 から 255 および -25 から -256 は、追加の uint8_t のみで表現されなければなりません。

    • 256 から 65535 および -257 から -65536 は、追加の uint16_t のみで表現されなければなりません。

    • 65536 から 4294967295 および -65537 から -4294967296 は、追加の uint32_t のみで表現されなければなりません。

  • 主タイプ 2 から 5 における長さの表現は可能な限り短くなければなりません。これらの長さの規則は、上記の整数の規則に従います。

  • すべてのマップのキーは、値の低いものから高いものへソートされなければなりません。ソートは、主タイプの 3/5 ビット分割に注意を払わずに、キーデータ項目の表現のバイトに対して行われます。(この規則は異なる型のキーを持つマップを許容しますが、それはおそらく一部の正規化実装でエラーにつながる可能性のある悪い習慣です。) ソート規則は次のとおりです:

    • 2 つのキーの長さが異なる場合、短いほうが先にソートされます。

    • 2 つのキーの長さが同じ場合、(バイト単位の) 辞書順で値が小さいほうが先にソートされます。

  • 不定長項目は確定長項目にされなければなりません。

プロトコルが IEEE 浮動小数点数を許容する場合、追加の正規化規則が必要になる可能性があります。規則の 1 つの例として、すべての浮動小数点数を 64 ビット浮動小数点数として開始し、32 ビット浮動小数点数へのテスト変換を行い、結果が同じ数値であれば短いほうの値を使用し、16 ビット浮動小数点数へのテスト変換でこのプロセスを繰り返すというものがあります。(この規則は、正および負の Infinity についても 16 ビット浮動小数点数を選択します。) また、NaN には多くの表現があります。NaN が許容される値である場合、それは常に 0xf97e00 として表現されなければなりません。

CBOR タグは正規化に関して追加の考慮事項をもたらします。正規形式におけるタグの有無は、プロトコルにおけるタグの任意性によって決まります。任意の場所でのオプションのタグ付けを許容する CBOR ベースのプロトコルでは、正規形式はそれらを許容してはなりません。特定の場所でタグを要求するプロトコルでは、そのタグは正規形式に現れる必要があります。正規化を使用する CBOR ベースのプロトコルは、代わりに、メッセージに現れるすべてのタグはオプションであるかどうかに関係なく保持されなければならないと定めることもできます。

3.10. strict モード​

CBOR の一部の適用領域では、正規化 (セクション 3.9) は不要ですが、潜在的に悪意のあるデータが存在する場合でも、異なるデコーダーが同じ (意味的に等価な) 結果に達することが要求される場合があります。これは、あるアプリケーション (ファイアウォールやその他の保護エンティティなど) が、独立にデータをデコードする別のアプリケーションが依存するデータに基づいて判断を下す場合に必要になることがあります。

通常、曖昧にデコード可能なデータを避けるのは送信者の責任です。しかし、送信者が攻撃者であり、異なるデコーダーによって異なる解釈をされるような CBOR データをわざと作成し、それを脆弱性として悪用しようとする可能性があります。これが問題になる可能性のあるアプリケーションで使用されるジェネリックなデコーダーは、受信者にも曖昧にデコード可能なデータを拒否する責任がある strict モードをサポートする必要があります。CBOR をデコードするファイアウォールやその他のセキュリティシステムは、strict モードでのみデコードすることが期待されます。

strict モードのデコーダーは、他のデコーダーによって異なる方法で解釈される可能性のあるデータを確実に拒否します。それは構文エラーを持つデータ項目を確実に拒否します (セクション 3.3)。また、他のデコードエラーを確実に検出する努力を払います (セクション 3.4)。特に、strict デコーダーは、次のいずれかを含む CBOR データ項目についてエラーを報告する (データを返さない) API を持つ必要があります:

  • 同じキーを持つエントリが複数あるマップ (主タイプ 5)

  • 間違った型のデータ項目に使用されているタグ

  • 与えられた型に対して誤った形式のデータ項目 (無効な UTF-8 や、付けられている特定のタグで解釈できないデータなど)

strict モードのデコーダーは、認識しないタグやシンプル値に遭遇したとき、次の 2 つのうちいずれかを行うことができます:

  • エラーを報告する (データを返さない)。

  • 未知の項目 (型、値、およびタグの場合はデコードされたタグ付きデータ項目) を、デコーダーがそのタグまたはシンプル値を認識しなかったという指示とともに、デコーダーを呼び出しているアプリケーションに出力する。

後者のアプローチは非 strict なデコーダーにも適しており、呼び出し側アプリケーションと同時にエンコーダーを更新する必要性なしに、新しく登録されたタグやシンプル値との前方互換性をサポートします。(このため、デコーダーの API には、呼び出し側アプリケーションがプログラムに適した方法で扱えるよう、未知の項目をマークする手段が必要です。)

この処理の一部には無視できないコストがかかる可能性があるため (特にマップの重複検出において)、strict モードのサポートはすべての CBOR デコーダーに対する要件ではありません。

一部のエンコーダーは、曖昧さなくデコード可能な CBOR が結果として得られるように入力データを提供することをアプリケーションに依存します。ジェネリックなエンコーダーも、アプリケーションが API 準拠のデータを提供しているかどうかに関係なく、出力を曖昧さなくデコード可能な CBOR に確実に限定する strict モードを提供することを望む場合があります。