証明パッケージ仕様 tel050jp-communication-proof-v1

この文書は、TEL050JP の「証明付保管」が発行する証明パッケージの技術仕様です。識別子 tel050jp-communication-proof-v1 で示される規則を、第三者が独自に検証器を実装できる粒度で定めます。背景となる考え方は仕組みの解説を、他の版は版の一覧を参照してください。

0. 識別情報

プロトコル識別子tel050jp-communication-proof-v1
パッケージ形式識別子tel050jp-communication-proof-package-v1(§10)
状態固定(frozen)。本版の規則は変更しない(§14)
固定日2026-08-30
後継の版なし(現行)
識別子が現れる場所communication.json の schema 欄、proof.json の protocol 欄、リーフと署名入力の LP(schema)、verify.html の PROTOCOL 定数

1. 適用範囲

定めるもの: 通信記録 Manifest の構造と正準化、通信記録ハッシュとリーフの計算、Merkle 木と監査経路、Symbol ブロックチェーンへ記録するメッセージの形式、発行者署名(proof.json と certificate.pdf)、証明パッケージ proof.json の構造、history.json、検証手順と判定コード。

定めないもの: 保管庫上の暗号化方式、購入・延長の業務フロー、certificate.pdf の見た目、Symbol ノードとの通信方法。これらは本版の互換性に影響しない範囲で変更されることがあります。

2. 記法

SHA-256(x)
FIPS 180-4 のハッシュ関数。出力は 32 バイト。
a ‖ b
バイト列の連結。
LP(s)
s を UTF-8 で符号化したバイト列の前に、そのバイト数を符号なし 16 ビット big-endian で置いたもの。65,535 バイトを超える s は拒否する。
UINT64_BE(n) / UINT32_BE(n)
符号なし 64 / 32 ビット big-endian 整数。
hex
16 進表記はすべて小文字。ハッシュ値は 64 桁、Ed25519 公開鍵は 64 桁、Ed25519 署名は 128 桁。
時刻
RFC 3339 の UTC 秒精度 YYYY-MM-DDTHH:MM:SSZ のみ。epoch(t) はその Unix 時刻(秒)。
UUID
8-4-4-4-12 の小文字 hex。第 3 グループの先頭は 1〜8、第 4 グループの先頭は 8・9・a・b のいずれか。
0xNN
1 バイトの定数(ドメイン分離バイト)。

3. 通信記録 Manifest(communication.json)

1 件の通信を表す JSON オブジェクトで、次のフィールドを過不足なくもちます。未知のフィールドも欠損もすべて拒否します。

フィールド型制約
schemastringtel050jp-communication-proof-v1 に固定
proof_idstringUUID
tenant_idstring空白でない
object_uidstring空白でない。call:・voice_message:・fax: に通信 ID が続く
kindstringcall / voice_message / fax
directionstringinbound / outbound
started_atstring時刻
ended_atstring または null時刻。started_at 以降
duration_secondsinteger0 以上
dispositionstring空白でない
source_partystring または null—
destination_partystring または null—
artifactobject下表
retentionobject下表
previous_proof_leafstring または nullhex 64 桁。extension_sequence が 1 のとき null、2 以上のとき必須
artifact のフィールド型制約
sha256stringhex 64 桁。原本ファイルのバイト列の SHA-256
archive_ciphertext_sha256stringhex 64 桁。保管庫上の暗号化済みオブジェクトの SHA-256
size_bytesinteger0 以上
media_typestring小文字の type/subtype
archive_version_idstring空白でない
retention のフィールド型制約
extended_fromstring時刻
retain_untilstring時刻。extended_from より後
extension_daysinteger1 以上
extension_sequenceinteger1 以上。同じ通信に対する何回目の証明か

previous_proof_leaf は、extension_sequence が 1 のとき必ず null、2 以上のとき必ず直前の版のリーフ(§6)です。この対応が崩れた Manifest は拒否します。履歴の切断を隠せないようにするためです。

4. 正準化

  1. すべての文字列とすべてのキーを Unicode NFC に正規化する。
  2. JSON のキーを UTF-8 バイト昇順に並べる(キーは ASCII のみ)。
  3. 空白を入れない({"a":1,"b":2})。
  4. UTF-8 で出力し、非 ASCII 文字をエスケープしない。
  5. / をエスケープしない。
  6. 数値は安全整数(253 未満の整数)のみ。浮動小数点数と、数値の文字列化を禁止する。
  7. null と "" と欠損を区別する。
  8. 制御文字(U+0000〜U+001F、U+007F〜U+009F)をすべての文字列で拒否する。

正準化した Manifest のバイト列を canonical_manifest と呼びます。証明パッケージに同梱される communication.json は、このバイト列そのものです。規則 8 は、制御文字が証明書上で不可視であり、JSON エンコーダごとにエスケープ表現が異なるため、同一 Manifest が実装間で別のハッシュになる経路を塞ぐためのものです。

5. 拒否条件

すべての実装は、次のいずれかに該当する Manifest を拒否します。

  • 未知のフィールド、欠損したフィールド、schema の不一致(古い版として読み替えようとするものを含む)。
  • kind / direction の列挙外の値。
  • 大文字を含む hex、64 桁以外の hex。
  • RFC 3339 UTC 秒精度以外の時刻(+09:00、ミリ秒、存在しない日付)。
  • ended_at < started_at、retain_until <= extended_from。
  • extension_sequence == 1 と previous_proof_leaf == null の不一致。
  • 整数でない数値、文字列で送られた数値。
  • 制御文字を含む文字列、UTF-8 として不正な文字列。

6. ハッシュとリーフ

communication_record_hash = SHA-256(canonical_manifest)
artifact_hash             = SHA-256(原本ファイルのバイト列そのもの)

leaf = SHA-256(
  0x00 ‖ LP(schema) ‖ LP(tenant_id) ‖ LP(object_uid)
  ‖ communication_record_hash[32] ‖ artifact_hash[32]
  ‖ UINT64_BE(epoch(retain_until)) ‖ UINT32_BE(extension_sequence)
  ‖ previous_proof_leaf[32](null のときは全ゼロの 32 バイト)
)

LP の対象は NFC 正規化後の文字列で、canonical_manifest に入る文字列と同一でなければなりません。schema は本版では常に tel050jp-communication-proof-v1 です。リーフは Manifest 全体を知らなくても、proof.json がもつ 7 つの値(tenant_id、object_uid、communication_record_hash、artifact.sha256、retention.retain_until、retention.extension_sequence、retention.previous_proof_leaf)から再計算できます。

7. Merkle 木と監査経路

  • バッチ内のリーフ順は proof_id の昇順(バイト比較)に固定する。同一 proof_id の重複と、空のバッチは拒否する。
  • 親ノード = SHA-256(0x01 ‖ left[32] ‖ right[32])。
  • 奇数規則: ある段の要素数が奇数のとき、余った最後の 1 つは複製せず、そのまま上の段に昇格する。末尾を複製する方式は、異なるリーフ集合が同じルートをもちうるため採用しない。
  • リーフ 1 件のバッチのルートは、そのリーフ自身。
  • 監査経路は {"sibling": hex64, "position": "left" | "right"} の配列。昇格した段にはエントリがない。position は sibling の側を示し、left なら SHA-256(0x01 ‖ sibling ‖ current)、right なら SHA-256(0x01 ‖ current ‖ sibling) で次の値を得る。

例: リーフ A、B、C(この順)のバッチでは、第 1 段が [H(A‖B), C]、ルートは H(H(A‖B) ‖ C) です。A の監査経路は [{B, right}, {C, right}]、C の監査経路は [{H(A‖B), left}] です。

8. Symbol へ記録するメッセージ

tel050jp:proof:v1 kind=communication root=sha256:<root> batch=<batch_id>

固定 156 文字。フィールド順は固定、区切りは半角スペース 1 個、末尾に空白なし、hex は小文字、未知フィールドの追加は不可です。パーサは次の正規表現に一致しないものを拒否します。

^tel050jp:proof:v1 kind=communication root=sha256:[0-9a-f]{64} batch=<UUID>$

このメッセージは、TEL050JP の Symbol 署名アカウントから所定の宛先アドレスへ送る転送トランザクションのメッセージ欄に置かれます。証明パッケージは、そのトランザクションを含むブロックの高さがファイナライズ済みの高さ以下になってから発行されます。

9. 発行者署名

署名方式は Ed25519(RFC 8032)です。署名鍵は TEL050JP の Manifest 署名鍵で、Symbol トランザクションに署名する鍵とは別のものです。

proof.json の署名

signing_input = 0x02 ‖ LP("tel050jp-communication-proof-v1")
                     ‖ canonical_json(proof.json から signature を除いたもの)

canonical_json は §4 と同じ規則(NFC、キーの昇順、空白なし、UTF-8、スラッシュ非エスケープ)です。署名は proof.json の signature オブジェクトに入ります。

signature のフィールド値
algorithmed25519
public_keyhex 64 桁。署名した Ed25519 公開鍵
valuehex 128 桁。署名

署名入力の先頭 34 バイトは常に 02 001f 74656c3035306a702d636f6d6d756e69636174696f6e2d70726f6f662d7631(0x02、長さ 31、識別子の UTF-8)です。

certificate.pdf の署名

signing_input = 0x03 ‖ LP("tel050jp-communication-proof-v1") ‖ SHA-256(certificate.pdf のバイト列)[32]

certificate.pdf はダウンロード時に proof.json から描画されるため、proof.json の署名は PDF を対象にしません。代わりに、上の入力に同じ鍵で署名した certificate.sig.json を同梱します。

certificate.sig.json のフィールド値
algorithmed25519
signscertificate.pdf
sha256hex 64 桁。署名時の PDF の SHA-256
public_keyhex 64 桁
valuehex 128 桁

ドメイン分離バイト

バイト用途
0x00Merkle 木のリーフ(§6)
0x01Merkle 木の内部ノード(§7)
0x02proof.json の署名
0x03certificate.pdf の署名

10. 証明パッケージ proof.json(tel050jp-communication-proof-package-v1)

利用者が保持し、検証器が読む文書です。Manifest そのものは含まず、リーフの再計算に必要な値だけを含みます。

フィールド型内容
schemastringtel050jp-communication-proof-package-v1 に固定
protocolstringtel050jp-communication-proof-v1 に固定
proof_idstringUUID。Manifest と同じ
issued_atstring時刻。パッケージの発行時刻
tenant_idstringManifest と同じ
object_uidstringManifest と同じ
kindstringManifest と同じ
artifact.sha256stringhex 64 桁。原本ファイルの SHA-256
artifact.size_bytesinteger原本ファイルのサイズ
artifact.media_typestring原本ファイルのメディアタイプ
communication_record_hashstringhex 64 桁(§6)
retention.retain_untilstring時刻
retention.extension_sequenceinteger1 以上
retention.previous_proof_leafstring または nullhex 64 桁
merkle.leafstringhex 64 桁。この証明のリーフ
merkle.audit_patharray§7 の監査経路
merkle.rootstringhex 64 桁。バッチのルート
merkle.batch_idstringUUID
symbol.networkstring記録先の Symbol ネットワーク
symbol.transaction_hashstring記録したトランザクションのハッシュ
symbol.block_heightintegerそのトランザクションを含むブロックの高さ
symbol.finalized_heightinteger発行時点のファイナライズ済み高さ
symbol.signer_public_keystring記録した Symbol アカウントの公開鍵
symbol.recipient_addressstring転送先アドレス
symbol.messagestring§8 のメッセージ
signatureobject§9 の署名

proof.json(およびその symbol オブジェクト)に node_url フィールドがあってはなりません。証明が自分を検証するノードを指定することは禁止です。

ZIP の構成: proof.json、history.json(§11)、certificate.pdf、certificate.sig.json(§9)、verify.html(オフライン検証器)。通信記録つきの版では §4 の canonical_manifest そのものである communication.json を、原本つきの版では original.<拡張子> を追加します。

11. history.json

同じ object_uid に対する proof.json を retention.extension_sequence の昇順に並べた配列です。各要素の retention.previous_proof_leaf は直前の要素の merkle.leaf に等しく、先頭の要素だけが null です。sequence が飛ぶ履歴、鎖が切れた履歴は拒否します。

12. 検証手順と判定コード

検証器の入力は、proof.json、検証器自身の許可リスト(信頼する Ed25519 公開鍵の一覧、Symbol 署名者の公開鍵、宛先アドレス、ネットワーク)、任意で原本ファイル・communication.json・history.json・certificate.pdf と certificate.sig.json です。許可リストは証明の中の値ではなく、検証器に固定された値を使います。

  1. schema と protocol が既知の識別子に厳密に一致すること。それ以外は unknown_schema とし、以降の検査を行わない。
  2. node_url がないこと。
  3. signature.algorithm が ed25519、public_key が許可リストにあり、§9 の入力に対して署名が検証できること。
  4. artifact.sha256 が hex 64 桁で、原本ファイルがあればその SHA-256 と一致すること。
  5. §6 の式で proof.json の 7 つの値からリーフを再計算し、merkle.leaf と一致すること。
  6. merkle.audit_path に沿ってリーフから積み上げた値が merkle.root と一致すること。
  7. symbol.message が、merkle.root と merkle.batch_id から §8 の形式で組み立てた文字列と一致すること。
  8. symbol.signer_public_key・recipient_address・network が許可リストと一致すること。
  9. symbol.block_height > 0 かつ symbol.finalized_height >= symbol.block_height であること。
  10. (任意)信頼するノードに transaction_hash を照会し、署名者・宛先・メッセージ・ブロック高が一致すること。
  11. communication.json があれば、そのバイト列の SHA-256 が communication_record_hash と一致すること。
  12. history.json があれば、§11 の鎖が成立すること。
  13. certificate.pdf と certificate.sig.json があれば、PDF の SHA-256 が sha256 と一致し、§9 の入力に対して署名が検証できること。
判定コード意味
unknown_schema識別子が既知の版と一致しない
proof_names_its_own_node証明が検証用ノードを指定している
missing_signature署名がない、または方式が ed25519 でない
untrusted_signing_key署名鍵が許可リストにない
bad_signature署名が検証できない
bad_artifact_hashartifact.sha256 の形式が不正
artifact_does_not_match原本ファイルの SHA-256 が一致しない
leaf_does_not_match_its_fields再計算したリーフが merkle.leaf と一致しない
audit_path_does_not_reach_the_root監査経路を積み上げてもルートに一致しない
malformed_audit_path監査経路の形式が不正
anchor_message_does_not_carry_this_root記録メッセージがこのルートとバッチ ID を表していない
anchor_is_not_the_allowlisted_one署名者・宛先・ネットワークが許可リストと一致しない
anchor_is_not_finalizedブロック高がファイナライズ済み高さを超えている
chain_disagrees_with_the_proofノードの回答が証明の記載と一致しない
history_skips_a_version履歴の sequence が飛んでいる
history_chain_is_broken履歴の前後のリーフがつながっていない

13. テストベクタ

本版の 3 実装(PHP・TypeScript・Python)が共有するテストベクタの抜粋です。独自実装は、まずこれらの値を再現してください。

Manifest(call_first_extension)

{
  "schema": "tel050jp-communication-proof-v1",
  "proof_id": "3f2a1c64-6f5f-4d2e-9c1a-8b0d5e7a4411",
  "tenant_id": "tel050jp",
  "object_uid": "call:01JCX9K8T4V2QW3M5N7P8R9S0T",
  "kind": "call",
  "direction": "inbound",
  "started_at": "2026-08-30T04:15:07Z",
  "ended_at": "2026-08-30T04:18:22Z",
  "duration_seconds": 195,
  "disposition": "answered",
  "source_party": "+815012345678",
  "destination_party": "+815058109483",
  "artifact": {
    "sha256": "397c4d496b5be24cde8027f261de3adf418f1178fed7638e91d02cc90ccf0102",
    "archive_ciphertext_sha256": "7e2573be8f2b70bb144640f08ed1db7744841bcee81c43b6d7ce0801151ef844",
    "size_bytes": 320044,
    "media_type": "audio/wav",
    "archive_version_id": "3sL4kqtJlcpXroDTDmJ.rmSpXd3dIbrHY"
  },
  "retention": {
    "extended_from": "2026-08-30T05:00:00Z",
    "retain_until": "2027-08-30T05:00:00Z",
    "extension_days": 365,
    "extension_sequence": 1
  },
  "previous_proof_leaf": null
}

canonical_manifest:

{"artifact":{"archive_ciphertext_sha256":"7e2573be8f2b70bb144640f08ed1db7744841bcee81c43b6d7ce0801151ef844","archive_version_id":"3sL4kqtJlcpXroDTDmJ.rmSpXd3dIbrHY","media_type":"audio/wav","sha256":"397c4d496b5be24cde8027f261de3adf418f1178fed7638e91d02cc90ccf0102","size_bytes":320044},"destination_party":"+815058109483","direction":"inbound","disposition":"answered","duration_seconds":195,"ended_at":"2026-08-30T04:18:22Z","kind":"call","object_uid":"call:01JCX9K8T4V2QW3M5N7P8R9S0T","previous_proof_leaf":null,"proof_id":"3f2a1c64-6f5f-4d2e-9c1a-8b0d5e7a4411","retention":{"extended_from":"2026-08-30T05:00:00Z","extension_days":365,"extension_sequence":1,"retain_until":"2027-08-30T05:00:00Z"},"schema":"tel050jp-communication-proof-v1","source_party":"+815012345678","started_at":"2026-08-30T04:15:07Z","tenant_id":"tel050jp"}
communication_record_hash6143dd3478a04f2b4fa108c4cef10dcf6f1ac2bda78965eaeb21e09475afa271
leaf7fb734be444f24687d183f3118d6c9154645a0f4bc90ca393eb1666f698213e5

3 件のバッチ

batch_id7b2f0c18-9d3e-4a51-b6c7-2e8f4a0d1b39
proof_id の順(昇順)
0a1b2c3d-4e5f-4a6b-8c9d-0e1f2a3b4c5d
3f2a1c64-6f5f-4d2e-9c1a-8b0d5e7a4411
c7d8e9f0-1a2b-4c3d-8e4f-5a6b7c8d9e0f
リーフ(同じ順)
ede0abdbb444f6efd417b579ace33fd9bc51c941b2ff14f85e26d6f65070c7b3
7fb734be444f24687d183f3118d6c9154645a0f4bc90ca393eb1666f698213e5
8f78067d1eca3637c498647b4d991a61be214a8ab27d8a45c6ffc5b0eddd57a5
root4a702235fe4922a434f675e424d971fa486ec6c97114e2034b212e64480e804c
Symbol メッセージtel050jp:proof:v1 kind=communication root=sha256:4a702235fe4922a434f675e424d971fa486ec6c97114e2034b212e64480e804c batch=7b2f0c18-9d3e-4a51-b6c7-2e8f4a0d1b39

リーフ 1 件のバッチでは、ルートはそのリーフ自身、監査経路は空の配列です。

14. 版の管理

  • 本版は固定です。規則の変更が必要なときは新しい識別子(例: tel050jp-communication-proof-v2)を定義し、版の一覧に追加します。
  • 本版で発行された証明は、本版の規則のまま検証できる状態を維持します。
  • 検証器は識別子を厳密一致で照合し、知らない版や、古い版として読み替えようとする証明を拒否します。
  • §13 のテストベクタの値が変わる変更は、すべて新しい版を必要とします。

15. 参考規格

  • National Institute of Standards and Technology. Secure Hash Standard (SHS). FIPS PUB 180-4, 2015.
  • S. Josefsson and I. Liusvaara. Edwards-Curve Digital Signature Algorithm (EdDSA). RFC 8032, IETF, 2017.
  • A. Rundgren, B. Jordan, and S. Erdtman. JSON Canonicalization Scheme (JCS). RFC 8785, IETF, 2020. 本仕様の正準化は JCS と同趣旨ですが、数値を安全整数に限定し、制御文字を拒否する点でより狭い規則です。
  • G. Klyne and C. Newman. Date and Time on the Internet: Timestamps. RFC 3339, IETF, 2002.
  • B. Laurie, A. Langley, and E. Kasper. Certificate Transparency. RFC 6962, IETF, 2013. リーフと内部ノードのドメイン分離の先例。
  • NEM Group. Symbol Technical Reference. 2021. https://docs.symbol.dev/

本ページは技術仕様であり、法的効力や証拠としての評価について述べるものではありません。