zkat’s diary

技術ブログ

情報的健康の実現に関して思ったこと

情報的健康の実現に関する本を読んでいて、思ったことをメモするものです。

デジタル空間とどう向き合うか 情報的健康の実現をめざして (日経プレミア) | 鳥海不二夫, 山本龍彦 |本 | 通販 | Amazon

明らかに多くの人の情報的健康を害する不適切な情報(例:詐欺など犯罪に該当する広告)は、コンテンツモデレーションを強めプラットフォームから極力除去する対策を施すことが重要であると思います。 ただどのような仕組みを用いてもこの対策をすり抜け、利用者に届いてしまうコンテンツは生じるものと思います。しかも、コンテンツモデレーションの仕組みが優秀であるほど、すり抜けたコンテンツの信頼性は高まってしまうことが起きそうです。 その為、最終的には利用者側でも情報の良し悪しを判断するだけのリテラシーが必要になりそうです。

  1. 極力プラットフォームから除去すること
  2. 利用者自身のリテラシーを高めること

この2つの両立が重要になりそうと思いました。

MATTRのBBS+署名ライブラリを試してみる

概要

MATTRというニュージーランドの企業が開発している、ライブラリbbs-signaturesを使ってみたメモです。

背景

今だ大規模な応用は不明ですが、プライバシーに関する利点(下記)から近年BBS+はVerifiable Credential(以下、VC)の署名アルゴリズムとして使用され出しています。

なお、VCの署名アルゴリズムにBBS+を用いることについてはMATTRが初めに提唱したようです。

JSON-LD ZKP with BBS+は、ニュージーランドのMATTR社が2020年4月のIIWで発表した比較的新しい方式です。

www.iij.ad.jp

プライバシーに関する利点

選択的開示に対応している

個人の属性情報(例えば:氏名、年齢、住所、性別、所属先、年収、etc)を電子データで表現し、第三者に提示するシナリオに於いては目的の属性情報だけに絞り、選択的に自身の属性を開示できることがプライバシーへ配慮する上で重要です。酒類購入の年齢確認に際して、氏名や所属先、年収を開示する必要はないわけです。

BBS+で署名したVCでは特定の属性のみを開示し、且つその値がVC発行者によって認証された値であることを暗号学的な方法で担保します。

リンクが不可能

VCを元に自身の属性を提示する度に、なにか毎回不変な値も併せて含まれているとします。(例えば、VC発行者による署名値) この場合、その値がIDとして機能し提示された属性値が突合されてしまうこと(リンク)が可能となります。

例えば、Aさんには自身の住所を提示し、別のBさんには年収を提示したとします。それぞれの提示の際に不変な値が含まれている場合、AさんとBさんが結託することで住所当たりの年収が判明してしまうこととなります。

BBS+で署名されたVCではこのようなことが生じないようにすることができます。それぞれの提示の際に、不変となる値が含まれないようにすることができる為です。 一方でSD-JWTでは生じます。

使ってみる

リポジトリのREADMEに記載のコードを動かしてみます。

github.com

なお、このリポジトリはBBS+のプリミティブな機能実装となっており、実際にVCへの署名の為に用いる際には、これを応用した別のライブラリ jsonld-signatures-bbsの使用が必要です。

鍵の作成

楕円曲線BLS12-381を用いた鍵が作成されます。

const keyPair = await generateBls12381G2KeyPair();

console.log(keyPair.publicKey)
console.log(keyPair.secretKey)
/*
公開鍵↓
Uint8Array(96) [
  178, 241, 165, 193,  46,  53, 138, 187,  17, 220, 172,  27,
   67,  49, 171, 251, 130, 242,  76, 152, 184,  44, 179,  16,
   23, 103,  95,  99,  45, 183,  48, 131, 229, 247, 212,  56,
    7, 184, 235, 200, 129,  65,   0, 184, 221,  23, 125,  52,
   17, 200, 118, 178, 228, 118, 249, 151, 160, 228,  10, 144,
   43,   7, 134, 135, 143, 198, 200, 126, 201,  14, 169,   6,
  156,  49, 116, 138, 218, 169, 250,  90, 234, 119, 177, 233,
  171, 106, 153,  56,  83,  75, 111,  51, 137,  97, 170, 103
]

秘密鍵↓
Uint8Array(32) [
  103,  20, 117, 167,  97, 243,  75, 128,
  249, 142, 207,  26,  13,  14, 131, 247,
  182,   6, 147,  21,  22,  69, 201, 255,
  201, 229, 184, 149, 178, 213, 104,  75
]
*/

署名の作成

2つのメッセージに対して、署名を行います。ここで定義するメッセージのそれぞれが選択的開示の対象となる属性情報となります。 BBS+による署名は、以下の通り複数のメッセージを入力として受け取ります。

const messages = [
    Uint8Array.from(Buffer.from("message1", "utf-8")),
    Uint8Array.from(Buffer.from("message2", "utf-8")),
];

console.log(messages)

//Create the signature
const signature = await blsSign({
    keyPair,
    messages: messages,
});

console.log(signature)

/*
メッセージ↓
[
  Uint8Array(8) [
    109, 101, 115, 115,
     97, 103, 101,  49
  ],
  Uint8Array(8) [
    109, 101, 115, 115,
     97, 103, 101,  50
  ]
]

署名↓
Uint8Array(112) [
  153,  61,  54, 215, 116, 172,  10,  40,  66, 216,  98, 208,
  114,  85,  66,  71,  85,  50,  99, 233, 128,  83, 234,  24,
   94, 168, 249,  29, 129,  37, 140, 230, 166,  61, 159,  55,
  244, 181, 115, 165, 105,  54, 127, 219, 233,  26, 133, 181,
   97,  65, 229,  41, 252, 220, 112,  78,  38, 239,   6, 153,
  202, 130, 196, 144,  18, 197, 136, 173, 160, 231, 132,  54,
  139, 224, 157, 181, 128,  96,  13, 217,  20,  14, 202,  20,
   45,  32,  76, 112, 125,  76, 192,  97, 240, 118,  55, 215,
  166,  87,  69,  52,
  ... 12 more items
]
*/

署名の検証

Issuerの公開鍵と署名値、オリジナルのメッセージを元に署名を検証します。この署名の検証処理は、VCに記載されている本人(ホルダ-)が、Issuerから受け取ったVCが有効であるかどうかを確認するために行います。VCを提示された者(Verifier)が行う処理ではないことに注意が必要です。

const isVerified = await blsVerify({
    publicKey: keyPair.publicKey,
    messages: messages,
    signature,
});

console.log(isVerified)

/*
{ verified: true, error: undefined }
*/

プルーフの作成と検証

ZKPの技術を用いることで、選択的開示された値が認証されていることを確認できます。 ここでは、message1を開示しています。なお、2回に開示を行いそれぞれのproofの値が異なることを確認し、連結可能な値がないことをも確認してみます。

/* 1回目の開示・検証 */
const proof1 = await blsCreateProof({
    signature,
    publicKey: keyPair.publicKey,
    messages,
    nonce: Uint8Array.from(Buffer.from("nonce1", "utf8")),
    revealed: [0],
});

console.log("--- proof1 ---")
console.log(proof1)

//Verify the created proof
const isProofVerified1 = await blsVerifyProof({
    proof: proof1,
    publicKey: keyPair.publicKey,
    messages: messages.slice(0, 1),
    nonce: Uint8Array.from(Buffer.from("nonce1", "utf8")),
});

console.log("--- isProofVerified1 ---")
console.log(isProofVerified1)

/*
--- proof1 ---
Uint8Array(415) [
    0,   2,   1, 169, 122, 185,  38, 200, 101, 243, 184, 181,
   86, 142,  88, 175, 148, 221,  43,  53, 196, 211,  89,  87,
   34, 206, 133, 186, 252, 111, 247,  24, 181, 195,  50, 143,
  227, 140,  68, 154,  10, 154, 184,  86, 169,  49, 104, 128,
  185,  94,  62, 151,   4, 104,  54, 125,  67, 193, 131,  62,
  155, 225,  62, 215,  20,  86,  17, 204, 115,  59,  37,  76,
  101, 196,  38, 210, 234, 147, 103,  75,  59, 177,  89, 184,
   63, 120, 220, 139,  67, 180,  40, 180, 171,  10, 137, 176,
   62,  60,  19, 151,
  ... 315 more items
]
--- isProofVerified1 ---
{ verified: true, error: undefined }
*/
/* 2回目の開示・検証 */
const proof2 = await blsCreateProof({
    signature,
    publicKey: keyPair.publicKey,
    messages,
    nonce: Uint8Array.from(Buffer.from("nonce2", "utf8")),
    revealed: [0],
});

console.log("--- proof2 ---")
console.log(proof2)

//Verify the created proof
const isProofVerified2 = await blsVerifyProof({
    proof: proof2,
    publicKey: keyPair.publicKey,
    messages: messages.slice(0, 1),
    nonce: Uint8Array.from(Buffer.from("nonce2", "utf8")),
});

console.log("--- isProofVerified2 ---")
console.log(isProofVerified2)
/*
--- proof2 ---
Uint8Array(415) [
    0,   2,   1, 167, 148,  55, 142, 251, 176, 179, 130, 173,
  183, 138, 173,  67, 102, 100,  11,  57,  31,  71, 189,  32,
   22,  16, 200, 167, 163, 191, 137,   4, 142, 191, 181, 194,
  254,  84, 155,  99, 114, 175, 194, 108, 106,  79,  60, 133,
  105,  81, 191, 183, 143,  57,  11, 154,  74, 156, 148,  91,
   73, 207,  60, 141,  52,  79,  83, 221, 114,  25, 220, 104,
  155,  63, 244, 208, 192,  61, 180, 250,  31, 153,  83,  51,
  214, 165,  21, 136,  26, 144, 219, 106,  62, 255, 123, 194,
  200,  51, 190, 175,
  ... 315 more items
]
--- isProofVerified2 ---
{ verified: true, error: undefined }
*/

初回開示時のプルーフと2回目の開示でのproofのそれぞれで値が異なっていることも確認できました。

KDF(Key Derivation Function)とCSPRNG(Cryptographically Secure Pseudo Random Number Generator)の使分けについて

KDFもCSPRNGも共通鍵を生成するために使用できる仕組みです。しかし、どのように使い分けるべきかよく分かっていませんでした。 最近読んだ本、「現代暗号技術入門」にて使い分けに関する記載がありましたので、引用したいと思います。

www.amazon.co.jp

KDFは必ずしも一様の秘密を必要としないので(エントロピーが十分でありさえすれば)、鍵交換から秘密を導出するときに都合がよい。

一方で、CSPRNGの入力には一様にランダムな入力が必要であるとされていました。

CSPRNGやKDFの入力とする値(CSPRNGの場合は、シードとして)にどのような特性があるかに応じて(一様であるか否かに応じて)、使うべきアルゴリズムを決めるのがよさそうです。

この件以外についても、この本は暗号技術を応用するに当たってとても有益な情報がたくさん記載されているのでお勧めです。

BIMI(Brand Indicators for Message Identification)について

BIMIの概要

Seth Blank氏を中心に、2019年頃からIETFで検討されているインターネットドラフトです。

datatracker.ietf.org

メール送信者が指定するロゴをメール受信者側で表示できるようにすることで、なりすましメールでないことを視覚的に判別できるようになります。 単なるロゴ表示機能だけではなく、無関係な第三者によるロゴの使用を防ぐための仕組みも提供します。

BIMIを利用する為の前提

メールのなりすましを防ぐ既存技術、DMARCが設定済みであることが必要です。DMARCは更に別の技術、SPFDKIM から成り立つものです。 BIMIに関係して、依存する既存技術を整理すると以下のようになります。

BIMIの導入により、ロゴを表示できるようになるのはもちろんですが、DMARCの普及によるメールセキュリティの改善も見込まれます。

BIMIの全体像とロゴ不正利用の防止

BIMIを用いた際のメール送受信に関する処理を以下のように図示します。 BIMIで特徴的なのは⑤の処理だと思います。メールの送信者がそのロゴを使用するにふさわしい者であることを証明する証明書が存在します。メールの受信者は、この証明書を検証することでロゴが適正に利用されていることを確認することができます。この証明書のことを、VMC(Verified Mark Certificate)といいます。

VMCの発行は、現在DigiCertEntrustが行っているようです。この記事では、以降VMCについて深堀します。

VMCの発行について

以下の情報に対して、認証局が審査を行いお墨付きを与えたものです。

  • ロゴデータ(SVG形式)
  • 使用者の情報(Subject)
  • どこで使うか(ドメイン

認証局にてどのような審査が行われるかについては、BIMIグループの資料を確認することで知ることができます。

https://bimigroup.org/resources/VMC_Requirements_latest.pdf

面白いところとしては、p42の3.2.16 Registered Mark Verificationにて、ロゴを認証する方法についての項目が存在するところかと思います。 申請者はしかるべきTrademark office(日本であれば、特許庁文化庁が記載されていますね)にて商標を登録し、その商標登録番号を認証局に伝える必要があるようですね。

www.wipo.int

VMCを実際に見てみる

amazon.co.jpは、BIMIに対応しているようなので、実際に証明書の内容を見てみます。まずは、証明書がどこで公開されているかをDNSをひくことで確認します。

$ dig +short txt default._bimi.amazon.co.jp
"v=BIMI1;l=https://d3frv9g52qce38.cloudfront.net/amazondefault/order_329474121_logo.svg;a=https://d3frv9g52qce38.cloudfront.net/amazondefault/amazon_web_services_inc.pem"

a=の値として記載されているURLがVMCの在り処となります。これをダウンロードして、解いてみると以下のようになります。 DigiCertが発行したVMCを用いていることが分かります。

また、OIDの1.3.6.1.5.5.7.1.12を私の環境では正しくパースできていませんが、ここにBIMIのロゴデータがSVG形式で入っているものと思われます。

Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            05:ba:54:0c:24:1c:f1:ff:9e:9a:77:f8:fb:82:bc:a8
        Signature Algorithm: sha256WithRSAEncryption
        Issuer: C = US, O = "DigiCert, Inc.", CN = DigiCert Verified Mark RSA4096 SHA256 2021 CA1
        Validity
            Not Before: Dec 23 00:00:00 2022 GMT
            Not After : Jan 23 23:59:59 2024 GMT
        Subject: jurisdictionC = US, jurisdictionST = Delaware, businessCategory = Private Organization, serialNumber = 4152954, C = US, ST = Washington, L = Seattle, street = 410 Terry Avenue N, O = "Amazon Web Services, Inc.", CN = "Amazon Web Services, Inc.", 1.3.6.1.4.1.53087.1.13 = Registered Mark, 1.3.6.1.4.1.53087.1.3 = US, 1.3.6.1.4.1.53087.1.4 = 6178564
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                RSA Public-Key: (2048 bit)
                Modulus:
                    00:c4:65:37:70:ba:65:a8:71:5a:87:7e:3e:74:80:
                    c8:10:5f:05:86:d5:1f:25:3d:4b:37:d8:97:f8:e7:
                    f5:d3:f7:6d:0b:d1:53:90:84:64:d4:37:22:ad:1f:
                    de:0e:0e:de:11:fc:62:42:77:8a:ff:ca:ee:16:ee:
                    a5:20:13:a2:d4:f5:1c:4a:e6:25:25:81:d3:c3:7f:
                    ff:22:c9:a6:fd:c0:77:56:e7:8c:13:e0:b8:db:36:
                    6c:7a:44:27:a8:39:c2:3a:f4:5b:d8:a7:9e:0c:40:
                    5a:23:2b:17:39:50:f3:6e:65:63:1d:3e:65:4b:18:
                    27:26:12:74:1a:5b:d6:77:33:ac:2f:ca:cd:8a:d0:
                    92:da:bd:b8:a7:37:5e:80:cc:cf:24:13:f3:7a:78:
                    fd:d9:f7:79:17:fc:f6:f5:cc:81:c7:c3:bb:92:f7:
                    f5:60:8a:f4:37:41:23:d4:a4:d8:8e:08:cd:9e:c2:
                    ec:e8:0f:58:4e:3f:b9:37:c5:dc:9e:cf:c9:c5:43:
                    1e:e5:ef:87:f0:94:3b:9e:d8:d6:7a:fb:f4:c2:7f:
                    d4:25:53:f5:ec:1a:f2:be:81:2a:73:a7:3b:32:ab:
                    e3:55:6a:f5:bc:fd:d2:9b:4d:80:8e:a7:1f:90:84:
                    91:43:19:44:c6:94:cf:39:e6:30:af:13:d8:54:02:
                    79:7b
                Exponent: 65537 (0x10001)
        X509v3 extensions:
            X509v3 Authority Key Identifier:
                keyid:BE:9F:BD:8D:57:6D:95:B5:AD:63:C3:97:4E:AB:A8:84:5D:3A:07:F5

            X509v3 Subject Key Identifier:
                B3:C5:40:4E:B8:57:BE:0D:44:D9:EA:BD:05:88:04:2D:87:6F:27:78
            X509v3 Subject Alternative Name:
                DNS:amazon.fr, DNS:amazon.it, DNS:amazon.es, DNS:amazon.eg, DNS:amazon.in, DNS:amazon.co.jp, DNS:amazon.ca, DNS:amazon.cn, DNS:amazon.com.br, DNS:amazon.com.mx, DNS:amazon.com.au, DNS:amazon.nl, DNS:amazon.ae, DNS:amazon.sa, DNS:amazon.com.tr, DNS:amazon.sg, DNS:amazon.pl, DNS:amazon.se, DNS:amazon.com, DNS:amazon.co.uk, DNS:amazon.com.be, DNS:amazon.de
            X509v3 Extended Key Usage:
                1.3.6.1.5.5.7.3.31
            X509v3 CRL Distribution Points:

                Full Name:
                  URI:http://crl3.digicert.com/DigiCertVerifiedMarkRSA4096SHA2562021CA1.crl

                Full Name:
                  URI:http://crl4.digicert.com/DigiCertVerifiedMarkRSA4096SHA2562021CA1.crl

            X509v3 Certificate Policies:
                Policy: 2.16.840.1.114412.0.2.5
                  CPS: http://www.digicert.com/CPS
                Policy: 1.3.6.1.4.1.53087.1.1

            Authority Information Access:
                CA Issuers - URI:http://cacerts.digicert.com/DigiCertVerifiedMarkRSA4096SHA2562021CA1.crt

            X509v3 Basic Constraints: critical
                CA:FALSE
            1.3.6.1.5.5.7.1.12:
image/svg+xml0#0!0...+.........#.J.9.....$y.I.|...0......~data:image/svg+xml;base64,H4sIAAAAAAAACl1STY+bMBC976+wvFd78Iw/sKuQVbZSb5UqtXvpjRKSILEQBRR2++s7Jm2VVpZnMON5b96DzdPbay+u7WXqxqGSCEaKdmjGfTccK/ny7ZOOUkxzPezrfhzaSr63k3zaPmym6/G+jaT4UU/tl8t46Hq+NnfDuz5PUly7dnke3ypphBGYKG8pmHSYKnma5/OHoliWBRYL4+VYkDGmYGy5fRBiM3dz3253r/XPcdgUt1N+f2mbWTR9PTFG008apWDavpKPh8NBiqXbzyeeKjOd2u54mm+HYm0+1/Ppvpn+NpOlg22l2FfyM3oH3qnkwIaPaCM4hSYAlQrR8yYIuRh/P+/QRDBcvCXDCxUhJKeigzI1GsEjB1tqwKAtkFUIIWgCjDt0AQIz3JJZVwrgGd0Qv/Zg4p90q6InIJUM+NgQlMTQPBffh6gIrFMWyvj9XvJ/Iv91YJUc+DsGFXkGnpdYB4fgNdo1AnGIrFhDsFlOBEyc48qdNLisJakE5HQAlxSxeVE7CGxFAMx1575iKCFElRiKVaRsa3bHZj+QMiV4ThpVto/bY6kCMKOHMiiX0XkMt2rb5H9l+wsPfa8QxQIAAA==
            CT Precertificate SCTs:
                Signed Certificate Timestamp:
                    Version   : v1 (0x0)
                    Log ID    : 55:59:53:AE:30:96:00:80:6C:D2:EB:52:08:A6:C9:9E:
                                93:18:28:AC:10:56:B4:42:1C:55:36:15:4C:5F:75:AC
                    Timestamp : Dec 23 21:16:05.891 2022 GMT
                    Extensions: none
                    Signature : ecdsa-with-SHA256
                                30:45:02:21:00:B6:DF:61:82:C8:48:A7:03:E3:7A:CC:
                                82:38:D0:38:A7:99:74:45:A4:FB:32:EA:0F:90:A6:FD:
                                BC:C0:EE:67:52:02:20:31:86:87:F6:5C:31:16:EA:90:
                                D1:5F:A3:13:70:D9:25:E6:39:C1:04:D4:39:F9:4B:B7:
                                AD:73:6F:E6:D2:F1:CE
    Signature Algorithm: sha256WithRSAEncryption
         66:a4:1c:c8:d3:d7:e8:61:18:e3:43:7f:5c:4b:8e:43:56:18:
         8b:83:bd:a9:50:e8:1e:d1:0c:8c:96:bf:f0:be:94:b5:25:90:
         a1:56:e6:c6:47:ae:9b:eb:52:14:4a:f5:39:d5:4a:59:b3:e3:
         5e:a5:65:50:5e:35:d4:90:e0:07:ea:04:2d:8d:d3:7a:eb:1d:
         ad:ad:71:09:df:c1:ef:89:6a:bb:1f:13:2f:b2:d6:5e:36:c2:
         34:3a:42:f3:30:c2:7b:95:f6:cc:ac:77:91:1e:c9:91:3a:91:
         38:df:b9:9d:a0:d7:3d:ef:69:ba:30:c2:91:8a:e4:62:18:92:
         20:d3:39:8e:2c:62:ca:c0:74:bf:57:30:89:e8:f3:db:77:1f:
         9c:eb:e9:b0:6d:7a:5c:47:0c:1e:bc:15:43:65:4b:dd:64:d6:
         1a:4c:98:f8:2f:2f:14:24:8b:0b:25:1e:58:d9:c8:16:23:2d:
         45:97:7c:8e:6d:4c:7c:c4:2e:9b:5a:8e:58:57:49:99:05:b6:
         3c:6c:fa:05:f8:e5:8a:d0:49:10:35:78:17:c2:7e:e0:fd:0b:
         94:cf:4d:90:a3:a4:0d:77:cb:a1:54:32:c7:b4:7b:af:2d:db:
         9b:4d:f0:51:18:ab:42:87:fc:4d:60:d0:c2:85:22:b5:31:0b:
         90:78:c0:6b:17:f8:98:37:a1:fa:bd:14:16:1f:d2:ee:5d:a8:
         6b:a2:22:73:13:1b:1f:0b:9b:0c:4c:2d:5f:f4:7e:e1:28:9b:
         68:ad:1a:c9:a0:45:65:81:c9:80:04:a6:3b:7b:8e:26:9b:dd:
         76:5c:31:76:df:b7:50:b1:9a:ea:3a:38:77:cd:61:61:e4:e6:
         66:73:e2:d8:9a:c6:46:97:8d:0e:00:c0:e2:a7:9c:51:de:d3:
         4b:51:d0:6e:aa:e1:3d:9d:94:8d:fb:5c:f4:d6:78:8a:84:dd:
         f2:e1:aa:f6:1f:2f:77:fc:e6:68:2e:79:68:e8:31:aa:79:ea:
         1e:6b:c1:ec:c6:16:48:ed:d8:ce:40:95:96:45:da:fa:ab:30:
         58:91:d7:f7:08:ad:31:0e:d4:37:91:35:3c:71:fd:9c:5a:c2:
         1d:ae:b8:b5:46:6a:6c:e4:11:5c:fd:9d:74:26:ca:7e:44:c8:
         a2:88:70:ab:e5:7f:5c:a5:29:66:02:e6:fb:f3:7d:1b:e2:75:
         7e:34:f2:a7:bb:a8:30:bc:46:ff:f9:13:41:39:8a:f8:28:fa:
         11:66:1c:d0:4b:10:a7:b9:7a:66:99:57:b7:e1:29:ef:a5:24:
         4e:15:60:06:38:ed:c6:7b:44:02:54:58:60:d6:69:0e:cb:0c:
         b9:08:8c:a9:84:f4:58:63

VMCはどれくらい発行されているか

2023/02/25時点の情報を、Certificate Transparencyの仕組みを用いて調べてみました。

国別発行件数

件数
アメリ 1,805件
イギリス 553件
ドイツ 241件
オーストラリア 164件
インド 162件
日本 127件

その他の国含めた発行割合は以下の通りでした。

ちなみに日本の127件のうち、具体的な組織としては以下のようなところがありました。 Paidy, Inc.、Rakuten Group, Inc、株式会社ビューカード、SmartHR, Inc.、QUALITIA CO., LTD、

これから普及していってほしいですね。

なお、CTのログサーバーについては、先ほどのBIMIグループの資料のp.90のAPPENDIX F - CT LOGS APPROVED BY AUTHINDICATORS WORKING GROUPに記載があります。
現在では、DigiCertが運用するGorgonというもの1つだけのようですので、このログサーバーを調べました。

TLSAレコードでTrust Anchor Assertionを設定する

概要

  • 私の管理するドメイン名、mtcq.jpにTLSAレコードを設定してみました。
  • その時に調べたことなどをメモしておきます。

TLSAレコードでできること

TLSAレコードを設定することで、ドメイン名とサーバー証明書(以下、エンドエンティティ証明書)に関する情報をDNSで紐づけすることができます。

TLSクライアントが対象ドメイン名に対してTLS通信を試行する際に、この紐づけ情報をチェックすることができるようになります。 *1

紐づける方法(以下、Certificate Usage)は、4つが存在します。

Certificate Usage ドメイン名に紐づけする情報
0 対象ドメイン名に対してエンドエンティティ証明書を発行した認証局の情報
1 対象ドメイン名に対して発行されたエンドエンティティ証明書の情報
2 対象ドメイン名に対して発行されたエンドエンティティ証明書のルート証明書の情報
3 対象ドメイン名に対して発行されたエンドエンティティ証明書の情報(上述の1と異なり、自己署名証明書でよい)

なぜTLSAレコードが作られたか

エンドエンティティ証明書を発行する認証局が過去に起こした事故が関係しています。
現在ウェブで用いられているエンドエンティティ証明書は、技術的にはどの認証局がどのドメイン名に対しても発行することができます。
例えば、example.comの管理者はいつも認証局Aから証明書を発行していたとします。悪意のある第三者が何らかの方法でexample.comの証明書を 認証局B(システムが脆弱でクラックされてしまったなど)から得たとします。この場合、認証局Bから得た証明書も問題なく使えてしまうという課題があります。*2 *3

具体的な事故事例としては、オランダの認証局DigiNotarが過去に起こした事故などがあります。

internet.watch.impress.co.jp

TLSAレコードが存在することにより、あるドメインで使用されるべき本物の証明書がどれであるのかを明確にすることができますので、このような問題の発生を低減することができます。

なお、TLSAレコードはその性質上DNSSECが有効化されていることが必須となります。*4

CAAレコードとの違い

ドメインの管理者はCAAレコードを設定することで、自身のドメインに証明書を発行してもよい認証局を指定することができます。認証局は、証明書を発行するタイミングでCAAレコードを引き、もし自身を指定する情報が記載されていなかった場合は証明書を発行しない挙動をとります。 *5

CAAレコードが証明書を発行するタイミングで働くの対し、TLSAレコードはTLSの証明書チェーン検証のタイミングで働く違いがあります。

設定した内容

Let's Encryptのエンドエンティティ証明書を設定している私のドメイン mtcq.jp について、以下のTLSAレコードを設定してみました。 Certificate Usageは2番で、ルート証明書に対するアサーションを入れるものとなります。

$ dig +short _443._tcp.mtcq.jp. -t TLSA
2 1 1 0B9FA5A59EED715C26C1020C711B4F6EC42D58B0015E14337A39DAD3 01C5AFC3

なお、Shumon Huque氏が作った以下のツールを用いることで、TLSAレコードの内容を簡単に作ることができます。

www.huque.com

ただ念のため、レコードの内容が正しいかどうかチェックしてみます。 今回私は以下の設定でレコードを作成しています。

Certificate Usage Selector Matching Type
2 1(証明書のSubjectPublicKeyInfo部分の情報を用いる) 1(sha-256ハッシュで表す)

Let's Encrtyptのルート証明書のPEMを取得し、SubjectPublicKeyInfoの情報を保存します。 (241バイトからSubjectPublicKeyInfoの情報が始まっています)
抜き出した情報のsha-256をとればよいです。

$ openssl asn1parse -strparse 241 -in ISRG-ROOT-X1 -noout -out spki.der
$ sha256sum spki1.der
0b9fa5a59eed715c26c1020c711b4f6ec42d58b0015e14337a39dad301c5afc3

レコードは正しく作れていそうです。

TLSAレコードの面白いところ

今回試せていませんが、Certificate Usageの3がとても面白いと思いました。
これを用いれば、TLSAレコードで自己署名証明書を提供することができます。TLSAレコードは、DNSSECで署名されることが期待されますので、認証局のような余分なTTPを必要とせずに信頼の連鎖を構築することができます。別の機会に設定してみて、対応しているクライアントを探すなどしてみたいと思います。

TLSAレコードの課題

個人的に感じる課題についてです。

  • TLSAレコードをチェックするTLSクライアントがほとんど存在しない?
  • DNSSECが普及していない

*1:実態として、現状TLSAレコードをチェックしているTLSクライアント(ブラウザ)は少ない、または存在しないと思われます。

*2:そういった悪意のある証明書の存在に気付く手段として、CTという仕組みがあります

*3:意図せず悪意のある証明書を発行してしまうことを防ぐためにCAAという仕組みがあります。

*4:TLSAレコード自体が詐称されていないことは、DNSSECで担保する

*5:2017年9月から

CRLiteの仕組み:Bloom filterの偽陽性をなくす方法

はじめに

TLS証明書の失効情報を管理する新たな仕組みとして、CRLiteというものが考えられています。(以下の論文です) CRLiteの根幹部分は、偽陽性を排したBloom filterで成り立っています。
この記事では、どのようにしてそのような偽陽性のないBloom filterを作るのか記載します。

ieeexplore.ieee.org

まずは、Bloom Filterについて説明します。

Bloom filterについて

概要

Bloom filter(以下、BF)自体は1970年に考案されたデータ構造なだけあり、既に様々なウェブサイトにて解説がなされています。 このデータ構造の典型的な使い道の一つとして、ブラックリストがあります。あるデータが、許容されないリスト(ブラックリスト)に記載されているか否かを判別したいとき、これを用いることで高速に(どんなにブラックリストが大きくなっても定数時間で)判別を行うことができます。

但し、素のBFにはデメリットとして偽陽性(False Positive)があります。
偽陽性:本当はリストに含まれて いない データなのに、含まれて いる と判別してしまう。

なお、偽陰性はありません。
偽陰性:本当はリストに含まれて いる データなの、含まれて いない と判別してしまう。

言い換えると、「含まれている!」という判定をBFから得た時は、誤りである可能性があります*1が、「含まれていない!」という判定を得た時は、確実に正しいということです。

仕組み

適当なサイズの配列と、適当な個数のハッシュ関数*2を用意することでBFを構築することができます。たとえば、配列長8、ハッシュ関数2個(md4とmd5とします)の場合を考えます。

データの追加

appleという文字列を追加することにします。追加するデータに対して、先のハッシュ関数ハッシュ値を求めます。ハッシュ値(整数)は、配列のインデックスの為に使用します。ただ、ハッシュ値そのままでは長大ですから、配列長に収まる数となるように配列の長さで割った余りを求めます。

データ ハッシュアルゴリズム hash値 hash値を8で割った余り(インデックス)
apple md4 0x9443a56f029a293d8feff3c68d14b372 2
apple md5 0x1f3870be274f6c49b3e31a0c6728957f 7

求めたインデックスが指し示す配列の値を、1にしておきます。BFではこれでデータを追加したこととなります。

データの存在確認

先のBFに、データorangeが存在しているか確認してみます。まず、同様にハッシュ値を求めます。

データ ハッシュアルゴリズム hash値 hash値を8で割った余り
orange md4 0xb0e795cd4b8b3c8247a09cbcd6f4a76c 4
orange md5 0xfe01d67a002dfa0f3ac084298142eccd 5

求めたインデックスを用いて配列の値を確認し、全てが1である場合はデータが存在するということとなります。 そうでない場合、1つでも0の箇所があるなら、データorangeは存在しないこととなります。 今回の場合、配列[4]と配列[5]の値はどちらも0なので、orangeは存在しないこととなります。

性質

先の例のように、データを追加する時も検索するときも、必要なのは固定回数のハッシュ値の計算と配列参照だけです。そのため、どんなにデータが増えても処理時間に問題が生じることはありません。また、データの追加に伴って配列が長くなることもありません。これは、BFのメリットです。

一方で、orange以外のデータで偶然配列の番号が4,5となるようなものがあったとします。そうすると、誤ってBFにデータが含まれていると判断する原因になります。これは、データが追加され配列の中が埋まっていくほど発生しやすくなります。これは、BFのデメリットです。

改善

なお、配列長や用意するハッシュ関数の個数を調整することで、BFの偽陽性を減らすことはできます。ただし0にすることはできません。
BFで管理したいデータの見込み数や、偽陽性の確率を入力すると適切な配列長やハッシュ関数の数を計算してくれるサイトがありますので、こちらを参考にすると良さそうです。

hur.st

Filter Cascade Design

BFの偽陽性なくす方法について説明します。
残念ながらいつでも偽陽性をなくすことができる、というわけではありません。
BFを作成する時点で、BFに追加するデータと 追加しないデータ の全て(これを、 $ U $ とします)が分かっている場合、BFを多段に構築(カスケード)することで偽陽性をなくすことができます。

ここでは追加するデータの集合を $A$ とし、追加しないデータの集合を $B$ とします。 $ A \cup B = U, A \cap B = \varnothing $ であるとして、構築手順の例を説明します。

データの追加

  1. $ A $を用いて通常通り、素のBFを構築します。これを、$ BF_1 $ とします。
  2. $ BF_1 $ が判断を誤ってしまうデータを見つけ(そのようなデータは、 $B$の要素として存在することとなります)、これを $ FP_1 $ とします。
  3. もし、$ FP_1 = \varnothing $ であるなら、偽陽性のないBFの完成です(1段)。そうでないなら、次の手順を続けます。
  4. $ FP_1 $を用いてBFを構築します。これを、$ BF_2 $ とします。
  5. $ BF_2 $ が判断を誤ってしまうデータを見つけ(そのようなデータは、 $A$の要素として存在することとなります)、これを $ FP_2 $ とします。
  6. もし、$ FP_2 = \varnothing $ であるなら、偽陽性のないBFの完成です(2段)。そうでないなら、次の手順を続けます。
  7. $ FP_2 $を用いてBFを構築します。これを、$ BF_3 $ とします。
  8. $ BF_3 $ が判断を誤ってしまうデータを見つけ(そのようなデータは、 $FP_1$の要素として存在することとなります)、これを $ FP_3 $ とします。
  9. もし、$ FP_3 = \varnothing $ であるなら、偽陽性のないBFの完成です(3段)。そうでないなら、次の手順を続けます。 (以下、省略)

このようにして、多段のBFを作成します。この例では、$ FP_3 = \varnothing $であったとして3段のBFを作成したものとします。

データの存在確認

上記で作成した多段のBFに順々に問い合わせを行うことで、確認します。

*1:BFを構築する際のパラメータを調整することで実用的なレベルまで誤りの可能性を減らすことができます

*2:必ずしもSHA-256などの暗号学的ハッシュ関数である必要はありません

Web Key DirectoryでPGPの公開鍵を配布する

概要

次のインターネットドラフト(以下、I-D)に基づいて、PGPの公開鍵を配布してみたので、そのメモを残します。

datatracker.ietf.org

WKDとは

Web Key Directoryの略で、PGPの公開鍵をHTTPSで公開する仕組みです。 PGPの公開鍵を公開する方法はすでに他のものが様々ありますが、それらがもつ課題の解決を目指しているようです。I-Dには、以下のようなことが記載されていました。

既存の方法 課題
DNSで配布する方法 必ずしもメールプロバイダがDNSの管理をしているわけではない
始めのメールに公開鍵を添付する方法 始めのメールが改ざんされる可能性がある
鍵配布サーバーを用いる方法 メールアドレスと鍵の対応性が正しく管理できない場合がある

公開用のURL

公開用のURLは、directとadvancedと呼ばれる2つの形式があるようですが、いずれかのURLでバイナリ形式の公開鍵を公開すればよいです。I-Dに倣い、メールアドレスがJoe.Doe@example.orgである場合のURLの例を以下に記載します。

direct method

https://example.org/.well-known/openpgpkey/hu/iy9q119eutrkn8s1mk4r39qejnbu3n5q?l=Joe.Doe

無作為に見える文字列、iy9q119eutrkn8s1mk4r39qejnbu3n5qは、メールアドレスのローカルパートであるJoe.Doeを変換することで作る文字列となります。 変換の方式ですが、以下で行うことができます。

  1. 小文字にする(Joe.Doe → joe.doe)
  2. sha1でハッシュをとる(joe.doe → a83ee94be89c48a11ed25ab44cfdc848833c8b6e)
  3. ハッシュした値(バイナリ)を、Z-Base32でエンコードする(a83ee94be89c48a11ed25ab44cfdc848833c8b6e → iy9q119eutrkn8s1mk4r39qejnbu3n5q)

advanced method

direct methodとほとんど一緒ですが、openpgpkeyというサブドメインをつくる必要が生じます。

https://openpgpkey.example.org/.well-known/openpgpkey/example.org/hu/iy9q119eutrkn8s1mk4r39qejnbu3n5q?l=Joe.Doe

公開の仕方

I-Dでは、上記のURLに公開鍵を配置するまでの仕組みが、Web Key Directory Update Protocolとして記載されています。

draft-koch-openpgp-webkey-service-14

ただ、お手軽に試すには少々時間が掛かりそうな内容ですので、手動で公開することにしました。 まずは、公開するためのPGPの鍵ペアをThunderbirdで作り、適当なファイルにエクスポートします。

thunderbird-pgp-key-export

エクスポートしたファイルの中には、-----BEGIN PGP PUBLIC KEY BLOCK----------END PGP PUBLIC KEY BLOCK-----で囲まれたデータが記載されています。 このデータの形式はASCII Armoredであり、以下で述べられている通りバイナリに変換する必要があります。

The HTTP GET method MUST return the binary representation of the OpenPGP key for the given mail address.

フッターとヘッダーを取り除き、そのまま変換したくなりますが、データの末尾にはCRCを示す情報が付加されているのでそれを削除してからbase64 -dなどでバイナリにします。 そのファイルをサーバーにアップロードすればよいです。

正しく公開できたか確認する

こちらのツールを利用することで、正しい形式の鍵が公開できているか確認することができました。

metacode.biz

メールソフトで利用する

このあたりに記載されているメールソフトを利用することで、WKDで公開鍵を取得し検証等に利用してくれるはずですが、確認はできていません。

https://wiki.gnupg.org/WKD#Implementations

ThunderbirdはEnigmailなどのアドオンを用いることなく内蔵の機能でpgpメールを送れるようにりましたが、WKDについてはまだアドオン使わないといけない感じでしょうかね。