2020年3月22日日曜日

BI-SGX開発&SGXとの戦闘記録(後編)

こちらは前回記事の続きです。


さて、前回記事ではIntel SGXに対して猛烈な怒りをぶち撒け、SGXを制圧する為にBI-SGXの開発に踏み込んだ、という話をしました。


BI-SGXは、「Bioinformatic Interpreter on SGX-based Secure Computing Cloud」の略で、名前の通り特に生命情報解析に特化したSGXベースの秘密計算クラウドです。何故生命情報をターゲットにしたかというと、所属していた研究室柄というのもありますが、遺伝子情報を始めとした生命情報が「特にバレるとヤバい情報」だから、という理由があります。


いや、折角SGXをブチのめして誰でも使いやすいクラウドを作るなら、最も苛烈な条件のデータを保護してみたくなるじゃないですか。


という事で、BI-SGXでは、BI-SGXに自分のデータをストアする、つまりBI-SGXをクラウドストレージとして利用するデータ所有者」と、そのデータを用いて秘密計算(厳密な「秘密計算」の定義とはわずかに異なる可能性もありますが)を行い、匿名化された統計情報を得ようとする研究者」の2つのタイプのユーザを想定し、それぞれに見合ったセキュアクラウドとしての機能を提供します。


以下に、BI-SGXの概要図を掲載します。


BI-SGXですが、前編で散々SGXをぶっ叩いた通り、「SGX特有の異常な難易度を解決する」事を非常に大きな目的としています。そこで考えたのは、「Enclave内インタプリタを駆動させれば、SGXSDKとか言うカスを使わずにユーザが簡単にSGXのセキュリティを利用できるのでは?」という事です。


何故インタプリタを選択したかについては、ちゃんと理由があります。まず、Enclave(厳密にはEPC)には96MBという苛烈なサイズ制限があるので、あまり重いコンポネントをEnclave内に積みたくない、という理由です。よって、BI-SGXでは、インタプリタの中でもコンパイル結果をメモリ上に直接展開して通訳系ルーチンを介して実行するタイプではなく、直接メモリ上の内部コードに対して再帰的下向き構文解析をかけて実行する、スタックマシンベースの可能な限り軽量なタイプを採用しています。


また、インタプリタ方式を採用する事により、SGXの最大の弱点である「サイドチャネル攻撃」の中でも極めて強力な攻撃である、「Controlled-Channel Attack」(私は勝手に制御チャネル攻撃、と呼んでいます)から防御する事も目的としています。こちらの詳細は後述します。


後は、身も蓋もない話ですが、私自身言語処理系はズブの素人だったので、その上でEnclave内に言語処理系を搭載するという、当時からすれば「本当にこんな事できるのか?」というまさしく「未踏」な状態であったので、複雑すぎる言語処理系は実装できなさそう、と考えたというのもあります笑


インタプリタの実装に際しては、「明快入門 インタプリタ開発」という参考書に非常にお世話になりました。物理的な本は絶版ですが、電子版はまだ買えますので、気になる方は是非買ってみて下さい。


インタプリタを採用した理由の最後に、スクリプトコード(つまりEnclave内の言語処理系が解釈するコード)も保護したい、というものがありました。実は、これは前述の制御チャネル攻撃対策にも直結します。コンパイラを採用すると、コードが保護出来ないため、制御チャネル攻撃を回避するには非常に複雑かつユーザに不便を強いる機構を採用しなければなりません(その最たる例がSGX-BigMatrixというシステムです)。


さて、BI-SGXのインタプリタでは、独自に開発した(厳密には上述の書籍で実装されていた言語をSGX最適化しBI-SGXの目的に合うよう魔改造した)「Qliphoth」という言語を提供しています。以下がパワポで突貫工事で作成したQliphothのロゴです。

Qliphothはヘブライ語で「殻」という意味で、Enclaveが防護する殻っぽいから、って言うのが表向きの理由です。じゃあ裏は何だ、というと、個人的に好きなSCP財団というフィクションサイト繋がりの名前にしたかったからなんですね。


本当は最初、SCP-2000に出てくる「手順ラザルス-01」から取って「Lazarus」って名前にしようとしたんですが(Lazarus自体は聖書に出てくる人物の名前です)、あろうことかまさかのこの名前を使ったシステムが存在していたので、Lazarusの採用は見送ったわけです。


SCPにおける各記事は「ヤバいオブジェクトを収容するための特別収容プロトコルや概要を記載したデータベース」という形を取っており(何の話をしているんだ……)、そのオブジェクトのヤバさ具合に応じてクラスが付与されています(例:Safe, Euclid, Keter)。


その中で、SCP財団が切り札として使えるオブジェクトのクラスが「Thaumiel」なのですが、これをそのままつけても面白くない(し、Thaumielは悪魔の名前なので縁起も良くない)ので、悪魔Thaumielを構成要員としている「クリフォトの樹」から名前を取る事にしました。そう、この「クリフォト」こそが「Qliphoth」です。Qliphothなら、表向きには「殻の意」と言えたので、非常に都合が良かったですね。


独自の言語とした理由は、「アウトプットプライバシの保護」をより系統的に行えるようにするため、です。アウトプットプライバシの保護とは、例えば平均を計算すると言いながら、データ1つのみの平均を計算すると、普通に生データをぶっこ抜けてしまう、といったような、「プロトコルには従ってるけど結果としてまずい情報が抜き出せてしまう」、更に暗号分野的な言い方をすれば「セミオネストな攻撃」からデータを保護する、というものです。


例えば、BI-SGXではデータの集合体であるデータセットの単位よりも細かい粒度では、計算に使用するデータ所有者のデータを指定する事ができません。「これとこれの内積を取れば……」とか、「言語において完璧に阻止っていうのは難しい」というご意見もあったりするのですが、少なくとも現段階ではそのような処理は根本的に出来ないように仕上がっています(これに関しては実際にBI-SGXのコードを読んだり利用して頂くのが一番早い気はします)。


このインタプリタを主軸に、BI-SGXは「Simple, fast, secure」な秘密計算クラウドを実現すべく、「Analyzing, only better.」をモットーに開発されています。以下がよく未踏の発表で使っていたBI-SGXのパッケージデザインです。


これ、如何にもよく出来ているように見えますが、実はこれも結構なオマージュなんです笑


私、あまり聞こえはよくありませんが、Grand Theft Auto Online(GTAO)というゲームが滅茶苦茶に好きで、実に3500時間以上プレイしているくらいにはハマっています。で、
そのGTAOの要素として「The Doomsday Heist」というPvEモードがあります。


このモードは、主人公たちを裏切った、暴走するAIとその開発者から世界を救うストーリーなのですが、そのAI「Cliffford」のパッケージデザインがこれです:



そっくりやんけ


いやまあ、似せたので当たり前です。何回か発表してて気づく人いるかなぁ、思ったのですが、終ぞ確認している限りではClifffordのオマージュである事はバレなかった模様です。ちなみに、TwitterのID(@dd_cliffford)はDoomsDay_Clifffordの意味ですし、アイコンもClifffordのスクリーン上での姿になってます。


また、成果報告会やその他未踏における大きな会議で発表していたデモ動画のBGMも、クレジットを条件に使用できるDoomsday Heistの劇中曲でした。


小ネタはここまでにして、各コンポネントの説明に入りましょう。


遠隔認証・通信部分

BI-SGXでは、前編でも紹介しました、sgx-ra-sampleというIntelによって公開されているRemote Attestation(RA)のサンプルコードを魔改造して、フレームワークのベースとしています。


実は、SGXはBI-SGXの想定するモデルとは真逆で、クライアント側SGXマシンを有しサーバ非SGX側、というモデルを想定しています。


何故かというと、SGXは元々DRM保護のような使い方を想定されていたからです。つまり、例えばビデオ配信サービス(サーバ)が、SGXマシンを有するクライアントのEnclaveに映像を送り込んだり、そのEnclave内でDRM処理をさせる、といったような使い方がデフォルトなのです。


当然、sgx-ra-sampleもこのデフォルトのモデルに準じていますので、BI-SGXのようにサーバ側にSGXを置くモデルに応用するには、通信関係の部分をsgx-ra-sampleから完全に真逆にする必要があります。


これがPythonならいざ知らず、いつの時代やねんみたいな古代のライブラリを使い、しかも前編で紹介した通り鬼のようなクソさを誇る「msgio」が通信を司っているわけですから、ひっくり返すだけでも一苦労でした。


ひっくり返しさえすれば、クライアント(データ所有者や研究者)がクラウドサーバ(SGXマシン)をRAに則って検証し、また鍵交換を済ませる事で、TLS通信の準備を整えます。ちなみに、RAの詳細はこちらの自著記事を御覧下さい。


なお、RAの方向を逆にすると、ちょっとしたセキュリティ上の懸念点が出てきます。そちらに関しては、簡単な説明と対応策を後述します。


しかしIntel、このまま上手くやり過ごしてくれるわけがありません。


ある日、いつも通り開発をしデバッグをすると、RAの途中でエラーを起こすようになりました。HTTPのエラーコードを見ると、どうやらフォーマットエラーらしいのですが、何故急にそうなったのか全くわからないので、sgx-ra-sampleのissueでIntelの研究者に質問しました。


すると、RAを利用するにあたってAPI方式を採用したのに乗じて、無告知で受理するデータのフォーマットを変えやがったのです。


当該部分を直そうとするのですが、sgx-ra-sampleはIntelの書いたコードです。おわかりですよね。目で追って原因部分を修正できるような生易しい仕上がりにはなっていません。


で、どうしたかって?


全部ベースから書き直しだよ
ブッ○すぞIntel!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!



データ所有者用機能

データ所有者用の機能と言えば、言うまでもなくユーザ自身のデータのアップロードとダウンロード機能を提供する、セキュアクラウドストレージとしての機能です。ここでは、msgio.cppの提供するsend関数とread関数を自力で修正した関数を用いて通信を行います(RAではもはや手を出すのも面倒臭いので、元のゴミみたいなsendとreadを使っています)。


クライアント側では、交換したセッション鍵を用いてOpenSSLで128bit AES/GCM暗号化し、サーバに鍵・初期化ベクトル・GCMタグ・Base64化した暗号文(Base64化しないとmsgioのせいで厄介な事になります)・Base64化する前の暗号文長(これもmsgioの魔の手から回避する為の手段です)を送信します。


サーバは、受信したらそれらをEnclave内に入れ、sgx_rijndael128GCM_decrypt()関数を用いて復号します。その後、シーリングという、暗号化に使う暗号コンテキスト、つまり暗号鍵や初期化ベクトル・GCMタグをCPUの回路素子(e-fuse)にストアでき、その暗号コンテキストは署名鍵またはEnclave固有のハッシュ値から導出される、という暗号化機能を使って暗号化します。


シーリングをしたら、Enclaveの外に出し、MySQLにストアしてアップロード完了です。署名鍵に固有なハッシュ値から暗号コンテキストを導出するポリシでシーリングを使う事により、Enclaveがデストラクト、あるいはセッションを切断してセッション鍵が消滅しても、無事に次回接続時以降でもデータを復号(アンシーリング)出来るようにしているのです。


ちなみに、新規登録・ログイン機能でも同様の手段を取っています。つまり、Enclaveにセッション鍵を使った暗号文の状態でユーザ情報を送り、新規登録あるいはログインの照合をします。この時はシーリングは用いておらず、パスワードにのみSHA-256ハッシュをかけてMySQLに保存しています。




……と、ここまでは序の口です


以上の議論は、十分小さいデータを処理する場合のみ当てはまります。何故かと言えば、EPCページは上限が96MBだからです。実際に生命情報解析でよく使われるVCF(Variant Call Format)ファイルは、1データで非圧縮で15.5GBにまで上る事も珍しくありません。実際に、BI-SGXのテストで用いた1000ゲノムプロジェクトの21番染色体のデータは、非圧縮でこのサイズがあります。


Enclaveに入るわけ無いですよね?


幸い、VCFは遺伝子上の1ポジションが1行に対応し、そのポジションについての各情報がタブで区切られています。この「各情報」には、そのポジションについての基本情報は勿論、遺伝子を提供した方の遺伝子情報(SNP)についての情報も含まれています。


1000ゲノムプロジェクトは公開前提に収集され提供されているデータですが、これがもしある大学機関が患者のゲノムデータを集めてアップロードしたものだったら?漏れて良いわけがありませんね。


結局、このバカでかいデータをEnclaveで扱うには、より小さい塊に分割するしかありません。BI-SGXでは、このEnclaveに収まり、十分統計計算も出来るサイズのデータの塊を「チャンク」と呼んでいます。Enclaveの中でチャンク分割するのは現実的ではないので、BI-SGXでは以下の手段を取っています:

  1. VCFを行単位で読み進めていき、所定のチャンクサイズ(BI-SGXでは20MB)に達したら読み込んだ分をVCFチャンクとし、セッション鍵により128bit AES/GCMで暗号化して吐き出す
  2. VCFを全て読み込むまで繰り返す
  3. 出来上がったチャンク群をtarアーカイブしたもの、そしてチャンクそれぞれに対する初期化ベクトル及びGCMタグを格納したバイナリ配列をサーバに送信する
図に表すと以下のような感じです。



サーバは受信したら、tarボール化されたVCFチャンクは展開して保存します。そして、初期化ベクトルとGCMタグのバイナリ配列やチャンク分割数、そしてデータ所有者が任意で登録できる遺伝子情報のメタデータ(人種や疾患情報等)をMySQL上のチャンク管理用テーブルにストアします。また、当然セッション鍵はサーバとクライアントで交換済みですので、サーバのEnclave側でセッション鍵をシーリングし、ファイルシステムに書き出して保存します。


また、チャンク群やセッション鍵は乱数的に生成された16文字の同じ接頭辞を持っており、かつその接頭辞は前述のMySQL上の管理テーブルにストアされるので、リンクが途切れてしまう事はありません。


このデータの読み出しについては、次の研究者用機能の項目で説明します。


研究者用機能

さて、いよいよ研究者用機能の説明ですが、これこそがBI-SGXの目玉であるインタプリタの部分です。


研究者は、Qliphothのスクリプトコードを、データ所有者が(小規模)データを上げるのと同等の手段でサーバにアップロードします。


すると、サーバはそのスクリプトコードを用いて、インタプリタの駆動を開始します。インタプリタの概要図は以下の通りです。


まず、構文解析器が、字句解析器を用いてトークン(自然言語における「単語」に相当する単位で、例えばif文なら「If」、変数なら「Ident」と言った感じで抽出します)を抽出し、より解釈しやすい「内部コード」を構築します。この時、トークン種別だけでは不十分な情報(例えば変数ならその格納されている値)や、関数のアドレス等を、内部テーブルに登録していきます。


内部コードが完成したら、コード実行部分が内部コードに対し再帰的下向き構文解析をかけて実行に移します。


再帰的下向き構文解析というと仰々しいですが、図に表すと以下のような感じです。


コードを文・式/項・因子という階層別の単位で再帰的に考え、トレースバックするイメージで解を取得するイメージです。


Qliphothの文法仕様や組み込み関数はこちらに記載しておりますので、気になる方は是非御覧下さい。


Qliphothの売りの一つとして、(個人で突貫工事で作った割には)多彩な組み込み関数を提供している、という点があります。例えば基本的な数学関数だったり、あるいはEnclave内では封印され、クソみたいな専用APIでしか提供されていない乱数生成機能をユーザフレンドリに提供する関数、そしてデータ所有者が上げたデータを用いた秘密計算を行う関数があります。


小規模データを用いた秘密計算には、現在の所
  • 整数データを用いた平均計算
  • 文字列(ゲノム列)を用いた編集距離計算
  • FASTAデータを用いたアラインメント計算
を提供しています。(まあこれらは正直実用性に乏しいですが……)


これらの処理は、条件に合うデータセットをMySQLから持ってきて、アンシーリングして計算し、匿名化された統計情報をリターンする、というごくシンプルな仕組みに従っています(これでも実装はSGXSDKのせいで相当大変でしたが)。


で、問題はデータ所有者の項目でも上げた通り、デカいデータを扱う関数の場合の話です。データ所有者がバカでかいVCFデータをアップロードしたので、当然研究者はそれを使って秘密計算を行いたいですよね。


そこで、生命情報解析向け組み込み関数として、ゲノムワイド関連解析(GWAS)関数をQliphothでは提供しています:
  • アノテーション検索
  • アリル頻度分析
  • フィッシャーの正確確率検定(FET)
  • ロジスティック回帰(LR)
  • 主成分分析(PCA)
アノテーション検索は、要するに求める遺伝子上のポジションに関する基本情報を照会するものですので、秘密情報は用いません。よって、dbSNPとClinvarという公開DBをダウンロードし、そこから照会する形を取っています。


その他のVCF秘密情報を用いたGWAS関数の動作の流れは以下の通りです。



アップロード時に既にチャンク分割をしているので、条件に当てはまるチャンク(データ所有者が任意で登録したメタデータで判断します)をひたすら逐次Enclaveに読み込んで行きます。


同時に、全てのチャンクの初期化ベクトルやGCMタグが詰まっているバイナリ配列から、対象のチャンクに対応するそれらを抽出し、Enclave内で復号します。そして、strtok_r()という古代兵器を用いて、VCFをパースしていきます。


VCFは前述の通り改行とタブで区切られているので、外側を改行をデリミタとしたstrtok_r()、内側をタブをデリミタとしたstrtok_r()として二重ループをぶん回す事で、各ポジションの各情報を抽出する事が出来ます(前編で述べた通り、streamがEnclave内では一切禁止されているので、これが考えうる限りでの最も効率的な方法でした)。


これにより、各GWAS処理に必要なデータを抽出し、目当ての統計計算を実現します(簡単に言っていますが、このそれぞれのGWASに対応したデータの抽出の実装は、本当に血の涙を流しながら実装していました)。



以上で、大まかなBI-SGXの説明が出来たと思います。より厳密な話は修論で書いているのですが、もし機会があればそちらも公開しようとは考えています。


また、Qliphothにはイースターエッグとして極秘の組み込み関数が存在するので、気になる方は是非ソースコードを読んで探してみて下さい(SGXSDKとか言うカスを使って実装しているので、絶対にオススメはしません)。



余談 - BI-SGXにおけるRA

本来、RAというのは非SGX側がビルドした(ビルドしている時点で非SGX側と言いながらSGXの環境は整っているんですが)Enclave用共有ライブラリをSGX側に配備し、RAにてちゃんと渡した共有ライブラリを駆動しているか、を検証するプロトコルです。


しかし、このモデルはSGX側が秘密情報を持っていない場合のみ有効です。何故ならば、本来のRAでは上記の通り、非SGX側がデプロイしてきた共有ライブラリをSGX側が検証する手立てが無いからです。


あくまでSGXはデータを保護する仕組みであり、コードのバグや悪意のあるコードを打ち消してくれる機能なんてものはありませんから、下手をすればSGX側のデータが悪意のある非SGX側の提供した共有ライブラリにより悪さをされるかも知れません。


ただ、BI-SGXではサーバ、つまりSGX側が秘密情報を持っています。よって、BI-SGXではSGX側、つまりサーバに共有ライブラリのビルドをさせ自分自身に配備させる方式を取っています。


こうなると、「非SGX側がコードを検証できないじゃん!」という意見が出ます。ごもっともです。そこで、現バージョンのBI-SGXではまだ対応しきれていないのですが、クライアントにサーバのイメージをDockerで与える事で、全く同じ環境でビルドする事を可能にし、検証可能性を実現します(RAにおいて正当性の判断基準とするMRENCLAVEという値は、同じソースから同じ環境でビルドすれば同値となるからです)。


このDockerを使うアイデアは、後で紹介する、未踏で同期で「準同型暗号によるバーチャルセキュアプラットフォーム」を開発されていた方によるアドバイスで確立されたものです。感謝。



余談 - BI-SGX v.s. 制御チャネル攻撃

ここで、高い火力を誇ると説明してきた「Controlled-Channel Attack(制御チャネル攻撃)」の説明をします。


まず、制御チャネル攻撃では第1段階として、対象とする保護システムを利用したアプリケーション(SGXアプリケーションが最たる例です)をオフライン解析(要するに攻撃者の手元で実験的に攻撃対象のアプリケーションを動作させる解析)します。そして、条件分岐先のどの変数や関数がどのページアドレスに乗るかを把握します。


次に、実際に攻撃対象が実行されたら、オフライン解析で得たページアドレスのページにアクセスされた瞬間、強制的にページフォルトが発生するように工作します。具体的には、例えば対象のページテーブルエントリの第51bit目を立てる事によって、これを実現します。


この状況で条件分岐が発生すると、当然当該ページアドレスにてページフォルトが発生します。こうなると、攻撃者は予め条件分岐先の変数や関数とページアドレスの対応を知っているため、どの関数や変数がアクセスされたかが分かります。


そうなると、条件分岐の方向を見ることで、条件分岐の判断基準となった秘密情報すらも抜き出せてしまう、というのが、制御チャネル攻撃の恐ろしい事です。


しかし、BI-SGXではインタプリタを採用している事により、この攻撃に対するある程度の耐性を備えています。何故ならば、「インタプリタはトークンの粒度でしか条件分岐処理を行わない」から、なのです。よって、仮にBI-SGXに制御チャネル攻撃を仕掛けても、攻撃者には以下のようにしか見えません。


こうなると、攻撃者は「何らかの変数が何らかの閾値と比較され、どちらかの方向にジャンプし何らかの変数/関数アクセスが行われている」、としかわからないのです。よって、BI-SGXは制御チャネル攻撃に対する一定以上に強固な防護性能を発揮できるのです。


ただ、コードの大まかな構成や、呼び出した組み込み関数の種類はバレてしまうので、それも守りたいのであればSGX-PCLというIntelによって公開されている拡張機能を使うのが良いでしょう。(Intel製なので私は絶対に手を出したくないですが)



まとめ

これで、BI-SGXの大まかな説明は出来たと思います。本当に地獄を見ながら開発していましたが、何とかまともな形に仕上げる事ができ、自分自身が一番安心しております。


SGXSDKとか言う悪魔の手にかからずSGXのハイパフォーマンスなセキュリティ機能を利用できるシステムとしてBI-SGXは我ながら優秀だと思うのですが、如何せん宣伝の仕方がわからないので、興味のある方は是非ご連絡いただきますと幸いです (手っ取り早いのはTwitterでの私のアカウントであります、@dd_clifffordへの連絡であると思います)。


最後に、同じ年度の未踏プロジェクトで、秘密計算という分野で共闘したプロジェクトをご紹介します。前述の通り、「準同型暗号によるバーチャルセキュアプラットフォーム」(KVSP)です。(スライドリンク:https://speakerdeck.com/nindanaoto/development-of-virtual-secure-platform


準同型暗号暗号とSGX、どうしても仲が悪いみたいな印象を受けがちなのですが、実はそもそも目指している領域が違う(準同型暗号は環境に左右されない絶対的な安全性、SGXはある程度シチュエーションを妥協した上でより現時点での技術という制約下での実用性を目指している)ので、根本的に敵対はしないのですが、何だかんだプロレス的に小競り合いを楽しませて頂きました笑


皆さん、突然ですが「エミュレータ」をご存知でしょうか。例えばPCでファミコンやプレステのゲームをプレイできるアレです。エミュレータは、当該ゲーム機のプロセッサを模擬してソフトウェア的に実装し、実際にゲームを動作させています。


KVSPの恐ろしいのは、そのエミュレータを「全て準同型暗号上で動かしてしまっている」所なんです。エミュレート対象はゲーム機ではなく、より一般的なコンピュータのCPUです。勿論、現代の技術が準同型暗号に追いついていないのでSGXほどの速度は出ませんが、準同型暗号は既存の暗号技術に対する脅威である「量子コンピュータ」に対抗できる格子暗号の一種らしいので、抜群の将来性を誇っているわけです。


私自身、実はこのKVSPのような準同型暗号ベースの技術がデファクトスタンダードになってくれる事を強く望んでいます。


何故ならば、このような準同型暗号ベースのシステムが実用的に普及すれば、SGXSDKみたいな悪魔に泣かされる人間が発生し得ないからです。