FPGA開発日記

カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages , English Version https://fpgadevdiary.hatenadiary.com/

NVDLAの内部構成調査(1. NVDLAのテストパタンの調査)

RISC-VのNVDLAがコラボしたり、NVDLAの調査も再度実施しなければならないと思ってきた。

riscv.org

しかしNVDLAの内部構成やインテグレーションの方法など、あまり情報が整理されていない。 というわけで、NVDLAのリポジトリのテストパタンを解析して、どうやって動いているのか見ていこう。

NVDLAには、主に以下のテストパタンが付属しており、Verilator, Vivado Simulator, VCSで動作させることができる。

verif/traces/traceplayer/sanity0
  • sanity0
  • sanity1
  • sanity1_cvsram
  • sanity2
  • sanity2_cvsram
  • sanity3
  • sanity3_cvsram
  • sdp_relu_int16
  • cc_alexnet_conv5_relu5_int16_dtest_cvsram
  • conv_8x8_fc_int16
  • googlenet_conv2_3x3_int16
  • pdp_max_pooling_int16

まずはsanityテストからチェックしようかな。

csr制御のシーケンサ

NVDLAのテストベンチは、read_reg, write_regなどのCSRを制御するシーケンスで構成されている。 これを実際に制御するのはNVDLAモジュールの傍にインスタンスされているcsr_master_seqというモジュールなのだが、このモジュールがそれぞれの命令をシーケンサで管理している。

表. NVDLAテスト用の制御シーケンサのステートマシン
コマンド名 番号
MSEQ_IDLE 8'h00
MSEQ_REG_RD 8'h01
MSEQ_REG_RD_WAIT_RESP 8'h02
MSEQ_REG_RD_POLL_WAIT 8'h03
MSEQ_REG_RD_MISMATCH 8'h04
MSEQ_REG_RD_TIMEOUT 8'h05
MSEQ_REG_WR 8'h08
MSEQ_REG_WR_WAIT_RESP 8'h09
MSEQ_REG_WR_TIMEOUT 8'h0a
MSEQ_MEM_RD 8'h10
MSEQ_MEM_WR 8'h18
MSEQ_MEM_LD 8'h20
MSEQ_MEM_DMP 8'h28
MSEQ_WAIT 8'h30
MSEQ_WAIT_TIMEOUT 8'h31
MSEQ_BOOT_1 8'hfd
MSEQ_BOOT_2 8'hfe
MSEQ_DONE 8'hff

sanity0

レジスタアクセスに関するテスト。

3回のレジスタアクセスを起こす。

  1. CFG_DST_SURF_0の初期値読み込み。
  2. CFG_DST_SURF_0に値を書き込み。
  3. CFG_DST_SURF_0に書き込んだ値が読み込めるか。

sanity1

BDMAに関するテスト。

tb.slave_mem_wrap.dbb_mem.memoryにsurf_memの内容をコピーする。このメモリは32ビット幅で、surf_data.txtの中身をそのまま格納している。

0x80000000に格納されているデータを0x81000000にコピーしている。

CFG_LINEを7に設定している。CFG_LINE_REPEATが0x7に設定されている。このパラメータは1surfaceにつき何ラインを移動するかを指定している。

最後にCFG_LAUNCH0に書き込みして実行開始。

64bit×32回のバーストが発生している。NVDLA側からリードリクエストを発生させ、読み込んだものをそのまま書き込み。

sanity2

BDMAに関するテストでsanity1と同じだが、最後に割り込みが発生するのを待つ。最後にwaitコマンドを実行し、割り込みが発生するまで待機する。

sanity3

sample_surf.datのデータと、weight.datのデータを使って畳み込み演算を実行している。

sample_surfのデータは0x80000000-0x80001000(4KB)、0x80100000から18KBに格納されている。といっても、かなり大量にレジスタを設定しているので、もう少し調査しないといけない。

NVDLAの扱えるデータ構造

sanity3は実際に畳み込みをするテストなのだが、どのようにデータを格納して扱うのだろうか?

色々調べると、詳細な情報を以下に見つけた。

  • NVDLA : In-memory data formats

In-memory data formats — NVDLA Documentation

どうやら、重みと入力データでデータタイプをいろいろ指定できるらしい。 NVDLAの精度としてはint8, int16, fp16をサポートしている。

入力データの形は、Width, Height, Channelの3次元構造を入力することができる。 これに対して、Line Stride, Surface Strideを指定することで畳み込みのサイズを指定している。

http://nvdla.org/_images/format_packed_feature_diagram.svg
図. 特徴データのデータ構造 (http://nvdla.org/hw/format.html#basic-weight-for-winograd-convolution より抜粋)
http://nvdla.org/_images/format_unpacked_feature_diagram.svg
図. 特徴データのデータ構造 (http://nvdla.org/hw/format.html#basic-weight-for-winograd-convolution より抜粋)

Intel Software Guard Extensionのチュートリアル サンプルプログラムを実行(Part-3 PasswordManagerCore)

Intel SGX Tutorialを読み進めているのだが、サンプルプログラムにも触れておきたい。

まずはPart-3のサンプルコードから。いわゆる普通のソフトウェアとか、GUIのソフトウェアとか触る機会がないので扱い方に少し戸惑うが...

software.intel.com

ページの一番最後にダウンロードのリンクがあるのでそれを経由してダウンロードする。

f:id:msyksphinz:20180827230914p:plain

Visual Studio 2017で開いた。メインプログラムはC#で記述されている。

f:id:msyksphinz:20180827231037p:plain

実行するとこのようになる。CUIのプログラムなのでテキストがパラパラと出てきて終了である。 が、最後FAILと表示されているじゃないか。何だこりゃ。

f:id:msyksphinz:20180827231131p:plain
=========================================================
NEW VAULT TESTS
=========================================================
Deleting test vault C:\Users\msyksphinz\Documents\test1.vlt
Creating new test vault C:\Users\msyksphinz\Documents\test1.vlt
    Expected 0, received 0...>>> PASS <<<
Unlock before creating a password
    Expected 8, received 8...>>> PASS <<<
Random password
    Expected 0, received 0...>>> PASS <<<
Add account before creating password
    Expected 8, received 8...>>> PASS <<<
Set master password
    Expected 0, received 0...>>> PASS <<<
Get number of accounts
    Expected 0, received 0...>>> PASS <<<
Found 0 accounts
    Expected 0, received 0...>>> PASS <<<
Set/add info at account index 3
    Expected 0, received 0...>>> PASS <<<
Get number of accounts
    Expected 0, received 0...>>> PASS <<<
Found 4 accounts
    Expected 4, received 4...>>> PASS <<<
Account 0:
    Expected 0, received 0...>>> PASS <<<
    Name =
   Login =
     URL =
    Expected 0, received 0...>>> PASS <<<
Password =
Account 1:
    Expected 0, received 0...>>> PASS <<<
    Name =
   Login =
     URL =
    Expected 0, received 0...>>> PASS <<<
Password =
Account 2:
    Expected 0, received 0...>>> PASS <<<
    Name =
   Login =
     URL =
    Expected 0, received 0...>>> PASS <<<
Password =
Account 3:
    Expected 0, received 0...>>> PASS <<<
    Name = IRC
   Login = jellybean
     URL = n/a
    Expected 0, received 0...>>> PASS <<<
Password =
Locking vault
Unlocking vault
    Expected 0, received 0...>>> PASS <<<
Get number of accounts
    Expected 0, received 0...>>> PASS <<<
Found 1 accounts
    Expected 1, received 1...>>> PASS <<<
Account 0:
    Expected 0, received 0...>>> PASS <<<
    Name = IRC
   Login = jellybean
     URL = n/a
    Expected 0, received 0...>>> PASS <<<
Password =
=========================================================
EXISTING VAULT TESTS
=========================================================
Checking for reference vault C:\Users\msyksphinz\Documents\reference.vlt
    Expected True, received False...>>> FAIL <<<
Hit ENTER to exit...

何をしているのか

  • TestSuite.cs
        public bool RunAll()
        {
           try {
                this.Initialize();
                this.RunNewVault();
                this.RunExistingVault();
           }
           catch {                
                return false;
           }
        }

mgr.vault_createによりパスワード管理クラス(PasswordManagerCore)を作成しているらしい。

  • TestSuite.cs
        PasswordManagerCore mgr;
...
            // Create a new vault
            Console.WriteLine("Creating new test vault {0}", path);
            rv= mgr.vault_create(path);

一つ一つ追っていくことにした。 コメントを追いかけると次のようなテストをしていることになる。

RunNewVault

  1. テスト用のvaultを作成する。
  2. vaultをアンロックする。
  3. ランダムなパスワードを作成する。
  4. パスワードなしでアカウントを追加する。これは失敗するはずである。
  5. スターパスワードを設定する。マスターパスワードは3.とは異なる。ここでは"12345@#$%asdfg"としている。
  6. アカウント数を確認する。ここでは4.で失敗したので0のままである。
  7. 再びアカウントを追加する。今回はマスターパスワードを追加したので成功する。
  8. アカウントを確認する。ここでは(何故か)4つのアカウントが追加された。最後のアカウントにのみ情報が入っている。
    Expected 0, received 0...>>> PASS <<<
    Name = IRC
   Login = jellybean
     URL = n/a
    Expected 0, received 0...>>> PASS <<<
  1. vaultのロックとアンロックを試行する。すると(これもなぜか)1つのアカウントが生き残る。上記のアカウントだ。

ここまでが、アカウントの作成テスト。

RunExistingVault

まずこのファイルの実行でFailしてしまうのだが、reference.vltを参照仕様として失敗している。これはサンプルプログラムのsample vaultというディレクトリに入っているので、C:\Users\msyksphinz\Documents\reference.vltに移動して実行してみる。

再び実行してみると、最後まで実行されてPassしたようだ。

まずはreference.vltだ。これは暗号化されている。これをどのようにしてテストするか、というところだが、

  1. 既存のreference.vltに対して、さらに新しいvaultファイルを作成する。
  2. スターパスワードを作成し、vaultのパスワードを更新する。
  3. アンロックせずにアカウント情報を読み込む。これはNoPermissionによりエラーが発生すr。
  4. アンロックせずにアカウントのパスワードを読み込む。これはNoPermissionによりエラーが発生すr。
  5. 間違ったパスワードでアンロックする。これはエラーとなる。
  6. 正しいパスワードでアンロックする。これは成功する。
  7. スターパスワードを設定する。
  8. パスワードやアカウントの情報などをいろいろ変更する。変更しても問題ないことを確認する。
f:id:msyksphinz:20180828001205p:plain
=========================================================
EXISTING VAULT TESTS
=========================================================
Checking for reference vault C:\Users\msyksphinz\Documents\reference.vlt
    Expected True, received True...>>> PASS <<<
Backing up original vault file C:\Users\msyksphinz\Documents\reference.vlt to C:\Users\msyksphinz\Documents\reference_orig.vlt
Create new vault on top of existing vault C:\Users\msyksphinz\Documents\reference.vlt
    Expected 5, received 5...>>> PASS <<<
Opening vault C:\Users\msyksphinz\Documents\reference.vlt
    Expected 0, received 0...>>> PASS <<<
Generate password
    Expected 0, received 0...>>> PASS <<<
Password: 3dxy6u6gTAiH5lbx
Set master password
    Expected 6, received 6...>>> PASS <<<
Change master password
    Expected 6, received 6...>>> PASS <<<
Read accounts without unlocking
    Expected 6, received 6...>>> PASS <<<
Read account password without unlocking
    Expected 6, received 6...>>> PASS <<<
Read account without unlocking
    Expected 6, received 6...>>> PASS <<<
Update account without unlocking
    Expected 6, received 6...>>> PASS <<<
Unlock with wrong password
    Expected 13, received 13...>>> PASS <<<
Unlock vault
    Expected 0, received 0...>>> PASS <<<
Set master password
    Expected 8, received 8...>>> PASS <<<
Change master password to itself
    Expected 1, received 1...>>> PASS <<<
Read accounts
    Expected 0, received 0...>>> PASS <<<
3 accounts
    Expected 3, received 3...>>> PASS <<<
Account 0:
    Expected 0, received 0...>>> PASS <<<
    Name = Google
   Login = johnm
     URL = http://google.com/
    Expected 0, received 0...>>> PASS <<<
Password = addition pack solar ring
Account 1:
    Expected 0, received 0...>>> PASS <<<
    Name = Intel
   Login = mechalas
     URL = http://intel.com/
    Expected 0, received 0...>>> PASS <<<
Password = GLV2TE@#P+FvMC?B9wYBz5rj
Account 2:
    Expected 0, received 0...>>> PASS <<<
    Name = Microsoft
   Login = nobody
     URL = http://miscrosoft.com/
    Expected 0, received 0...>>> PASS <<<
Password = O1d-Tongue 0pportunity-ball
Generating new password for account 0
    Expected 0, received 0...>>> PASS <<<
Password 0: #AnzRj4Daqerg11V
Changing account password for account 0
    Expected 0, received 0...>>> PASS <<<
Getting new password for account 0
    Expected 0, received 0...>>> PASS <<<
New password = #AnzRj4Daqerg11V
Comparing generated password #AnzRj4Daqerg11V to assigned password #AnzRj4Daqerg11V
    Expected True, received True...>>> PASS <<<
Generating new password for account 1
    Expected 0, received 0...>>> PASS <<<
Password 1: WfW7@q7o8x@@zQ*x
Changing account password for account 1
    Expected 0, received 0...>>> PASS <<<
Getting new password for account 1
    Expected 0, received 0...>>> PASS <<<
New password = WfW7@q7o8x@@zQ*x
Comparing generated password WfW7@q7o8x@@zQ*x to assigned password WfW7@q7o8x@@zQ*x
    Expected True, received True...>>> PASS <<<
Generating new password for account 2
    Expected 0, received 0...>>> PASS <<<
Password 2: cqYyZc7rWC$FV!yd
Changing account password for account 2
    Expected 0, received 0...>>> PASS <<<
Getting new password for account 2
    Expected 0, received 0...>>> PASS <<<
New password = cqYyZc7rWC$FV!yd
Comparing generated password cqYyZc7rWC$FV!yd to assigned password cqYyZc7rWC$FV!yd
    Expected True, received True...>>> PASS <<<
Fetching undefined account 5:
    Expected 0, received 0...>>> PASS <<<
    Name =
   Login =
     URL =
Fetch password for account 5 to clipboard
    Expected 0, received 0...>>> PASS <<<
Password =
Comparing empty password to undefined account password
    Expected True, received True...>>> PASS <<<
Copy password for account 5 to clipboard
    Expected 0, received 0...>>> PASS <<<
Copy password for account 105 to clipboard
    Expected 4, received 4...>>> PASS <<<
Copy password for account 1 to clipboard
    Expected 0, received 0...>>> PASS <<<
Copy password for account 2 to clipboard
    Expected 0, received 0...>>> PASS <<<
Write new account information for account 2
    Expected 0, received 0...>>> PASS <<<
Get new account information for account 2:
    Expected 0, received 0...>>> PASS <<<
    Name = AOL (should be AOL)
    Expected True, received True...>>> PASS <<<
   Login = aoluser (should be aoluser)
    Expected True, received True...>>> PASS <<<
     URL = http://aol.com/ (should be http://aol.com/)
    Expected True, received True...>>> PASS <<<
Write new account information for account 6
    Expected 0, received 0...>>> PASS <<<
Get new account information for account 6:
    Expected 0, received 0...>>> PASS <<<
    Name = ≪ταБЬ?σ≫ (should be ≪ταБЬ?σ≫)
    Expected True, received True...>>> PASS <<<
   Login =  (should be )
    Expected True, received True...>>> PASS <<<
     URL = http://unicodetest.net/ (should be http://unicodetest.net/)
    Expected True, received True...>>> PASS <<<
Change master password
    Expected 0, received 0...>>> PASS <<<
Lock the vault
    Expected 0, received 0...>>> PASS <<<
Read accounts  after locking
    Expected 6, received 6...>>> PASS <<<
Read account password after locking
    Expected 6, received 6...>>> PASS <<<
Unlock vault with old passphrase
    Expected 13, received 13...>>> PASS <<<
Unlock vault with new passphrase
    Expected 0, received 0...>>> PASS <<<
Read accounts
    Expected 0, received 0...>>> PASS <<<
4 accounts
    Expected 4, received 4...>>> PASS <<<
Account 0:
    Expected 0, received 0...>>> PASS <<<
    Name = Google
   Login = johnm
     URL = http://google.com/
    Expected 0, received 0...>>> PASS <<<
Password = #AnzRj4Daqerg11V
Account 1:
    Expected 0, received 0...>>> PASS <<<
    Name = Intel
   Login = mechalas
     URL = http://intel.com/
    Expected 0, received 0...>>> PASS <<<
Password = WfW7@q7o8x@@zQ*x
Account 2:
    Expected 0, received 0...>>> PASS <<<
    Name = AOL
   Login = aoluser
     URL = http://aol.com/
    Expected 0, received 0...>>> PASS <<<
Password = cqYyZc7rWC$FV!yd
Account 3:
    Expected 0, received 0...>>> PASS <<<
    Name = ≪ταБЬ?σ≫
   Login =
     URL = http://unicodetest.net/
    Expected 0, received 0...>>> PASS <<<
Password =
Copying modified vault to C:\Users\msyksphinz\Documents\reference_modified.vlt
Restoring original vault file from C:\Users\msyksphinz\Documents\reference_orig.vlt to C:\Users\msyksphinz\Documents\reference.vlt
Sleeping for 15 seconds to let clipboard timers finish...
Hit ENTER to exit...

vaultはどこに?

vaultクラスは結局PasswordManagerCoreNativeクラス内のvaultメンバとして定義されているのだが、

  • PasswordManagerCoreaNative.h
 Vault vault;

結局はキーを格納しているクラスにすぎない。

  • PasswordManagerCore.h
class PASSWORDMANAGERCORE_API Vault
{
    Crypto crypto;
    char m_pw_salt[8];
    char db_key_nonce[12];
    char db_key_tag[16];
    char db_key_enc[16];
    char db_key_obs[16];
    char db_key_xor[16];
    UINT16 db_version;
    UINT32 db_size; // Use get_db_size() to fetch this value so it gets updated as needed
    char db_data_nonce[12];
    char db_data_tag[16];
    char *db_data;
...

lock()unlock()はCryptoクラスにより制御されている。

int Vault::unlock(const char *password)
{
    crypto_status_t rv;
    char db_key[16];

    if (!this->is_valid()) return NL_STATUS_INVALID;
    if (!this->is_locked()) return NL_STATUS_OK;

    // Validate the passphrase by attempting to decrypt the database key

    rv = crypto.unlock_vault((PBYTE)password, (ULONG) strlen(password), (PBYTE)m_pw_salt, (PBYTE)db_key_enc, (PBYTE) db_key_nonce, 
        (PBYTE) db_key_tag, (PBYTE) db_key);

    if (rv == CRYPTO_ERR_DECRYPT_AUTH) return NL_STATUS_PASSWORD;
...

Freedom-U-SDKで生成したLinuxバイナリを自作RISC-Vシミュレータで実行 (3. シェル実行)

HiFive Unleashedは高くて買えないのだけれども、RISC-V SDKであるFreedom-U-SDKを使ってみることにした。

自作RISC-Vシミュレータを使ってRISC-VのLinuxをブートさせたいのだけれども、なかなか動作しないので四苦八苦していた。 #自作RISC-Vシミュレータは、そのうちGitHubに公開できるように調整中。

ログイン画面までは動いていたのだが、キー入力がうまくいかなくてそこから先に進んでいなかったのだった。 ターミナルののモジュールとか、Spikeとの動作の違いを見ながら進めていった結果、ログインしてからシェルが動くようになったぞ!

f:id:msyksphinz:20180829230543g:plain
図. 自作RISC-VシミュレータでLinuxを立ち上げログイン・シェルを動かす。

肝となったのは、デバイスにコマンドを渡すときにデバイスIDも一緒に渡すのだが、デバイスの処理が完了した際にFromHostにデータを戻すとき、同様にどのデバイスからのデータであるかを示さなければならないということだ。これを間違えていたため、いつまでたってもキーボードの入力値を受け付けなかった。

  void StdInRead () {
    int read_data;
    // DWord_t fromhost;
    // GetPeThread()->LoadFrom_FromHost (&fromhost);
    // if (fromhost == 0) {
    if ((m_req_count > 0) &&
        ((read_data = read()) != -1)) {
      read_data = 0x100 | read_data;

      // デバイスIDも一緒にFromHostに返す。
      uint64_t resp_data = static_cast<uint64_t>(GetDevId()) << 56 | read_data;
      GetPeThread()->StoreTo_FromHost (resp_data);
      GetPeThread()->DebugPrint("<Info: Tick read enable() = %x, resp_data = %016lx>\n", read_data, resp_data);
      m_req_count--;
      //}
    }
  }

とりあえずシミュレータのメンテナンスはここまでで終了かな。もう少し高速化しなければ... そのためには、今一番足かせになっているメモリアクセスのルーチンを高速化しなければ。

Google Perftoolsで解析すると、以下のようになっている。やはり、メモリアクセスが遅いんだな。

(pprof) top
Total: 19415 samples
    3750  19.3%  19.3%     5083  26.2% Memory::LoadMemory
    3088  15.9%  35.2%     6920  35.6% RiscvPeThread::WalkPageTable
    1457   7.5%  42.7%    19198  98.9% RiscvPeThread::StepExec
    1384   7.1%  49.9%     9034  46.5% RiscvPeThread::FetchMemory
    1185   6.1%  56.0%     1185   6.1% CsrEnv::Riscv_Read_CSR
    1173   6.0%  62.0%     1173   6.0% RiscvDec::DecodeInst
     872   4.5%  66.5%      872   4.5% __memmove_avx_unaligned_erms
     768   4.0%  70.4%      768   4.0% TraceInfo::RecordTrace
     543   2.8%  73.2%     2056  10.6% RiscvPeThread::CheckInterrupt
     397   2.0%  75.3%     3561  18.3% EnvBase::LoadMemoryDebug

MarkdownエディタならTyporaが最強であるという話

RISC-V関連の寄稿する機会も増えてきて、毎日のブログ投稿する場合にも使うのがMarkdownなのだが、MarkdownのエディタいうのもEmacsVim並の宗教戦争が起きそうな領域だと思っている。

Markdownエディタといえば有名どころを少し調べてみると、かつてはQiitaのKobitoだったりなど、最近はMicrosoft Visual Studio Codeだったりとか、AtomMarkdownエディタとして有名どころである。これらのエディタの優れている点は、Markdownを記述しながらリアルタイムにプレビューを確認できるところであると思う。

f:id:msyksphinz:20180825020722p:plain
図. 通常のMarkdownエディタは、編集ペインとプレビューペインが分割される形式をとっている。

しかし、私に必要なのはそこではないのだ。私はMarkdownの文法などは全く興味がない。ただ綺麗に、分かりやすく文章を表示できて、快適にMarkdownを記述できれば良いのだ。別に横に並べてMarkdownのテキストを確認するなどは、本当はやりたくない。

この中で、TyporaというMarkdownエディタは一歩抜きんでている。Typoraというエディタは、Markdownソースコードはデフォルトでは表示されない。

https://typora.io

Look & Feelは以下のような感じだ。これ以外にもいくつかのテーマが用意されている。モダンでカッコいい。

f:id:msyksphinz:20180825020428p:plain
図. Typoraの編集画面。Markdownソースコードは出てこない

Typoraにプレビューなどという概念は存在しない

Typoraのすごいところは、Markdownを記述したその場で整形されていく。つまり、すぐにプレビューがその場で確認できるのだ。

f:id:msyksphinz:20180825020506g:plain
図. Typoraは入力したMarkdownがその場で整形される。

Markdownの様々な要素を簡単に挿入できる

Typoraはそれだけではない。Markdownの表の埋め込みや、数式の挿入なども簡単にできる。

f:id:msyksphinz:20180825020556p:plain
図. Markdownの表の挿入もカンタンである。

数式の挿入もできる。

f:id:msyksphinz:20180825020606p:plain
図. Markdown中に数式を埋め込むことができる。

公式サイトにもマニュアルがあるので、こちらを参考にしよう。

Markdown Reference

快適なTyporaがhtml埋め込みに対応して最強状態に

これまでのTyporaの唯一の弱点は、htmlの埋め込みに非対応だったことだ。つまり、Markdownで色付けなどを行う場合には<span style="color:xxx">などと入力しなければならなかったのだが、Typoraはこれらの埋め込みをプレビューに反映することができなかった。

しかし、Version 0.9.54になって、HTMLの埋め込み要素まで対応できるようになった。これにより色付けなどの埋め込み要素もカンタンにプレビューできるようになった。

f:id:msyksphinz:20180825020627p:plain
図. Markdown中にhtmlを埋め込んでもそのまま表示できる。

Graphvizの形式の表記などにも対応している。もう、出来ないことは無いのではないかと思うくらいである。

f:id:msyksphinz:20180825020640p:plain
図. Markdown中にGraphvizのプレビューを行える。

まとめ: TyporaはMarkdownエディタとして非の打ちどころのない最強エディタ

オープンソースではないところが唯一の難点だが、それ以外は非常に満足しており。もうMarkdownエディタはTypora一択になっている。

次に対応してほしいのは、各種ブログサービスとの連携かな。例えば、はてなブログなどに一発で投稿できるようになって欲しい。

Intel Software Guard Extensionのチュートリアルを読む(2. Design an Application)

CPU脆弱性についての論文を読んでいると必ずと言っていいほど出てくるIntel SGX(Software Guard Extension)。 なんとなく秘匿データを扱うためのIntelのハードウェア機構なんだろうなと思っていたけど、あまり知ったかぶりをしているのもよくないのでチュートリアルを読んでみることにした。

前回に続いて、今回はTutorialのPart-3だ。いよいよPassword Managerの実装に入っていく。まずはクラス設計から、どこに境界を作成するのかについて考えていく。

software.intel.com


Intel® Software Guard Extensions Part-3: アプリケーションのデザイン

Part-3では、Intel® Software Guard Extensions (Intel® SGX)を使ってどのようにしてアプリケーションを設計するかについて説明する。Part-1ではSGXの考え方について説明し、Part-2ではTutorial Password Managerについて概要を説明した。ここでは、Intel SGXを使ったアプリケーションのおおざっぱな構成について概観enclaveを使用したデザインの設計に必要なクラスモデルを作成する。

チュートリアルの一覧については、Introducing the Intel® Software Guard Extensions Tutorial Seriesで見ることができる。

enclaveおよびenclaveインタフェースを使用したアプリケーションのコーディングはまだ行っていないが、ソースコードを提供している。ユーザインタフェースの無い非Intel SGXのアプリケーションコアはダウンロードすることができる。これは小さなテストプログラムであり、C#で記述されたコンソールで動作するプログラムと、サンプルのパスワードファイルで構成されている。

Enclaveのデザイン

Intel SGXを使用したTutorial Password Managerの設計では、以下の一般的なアプローチを取る。

  1. アプリケーションの秘密情報を決める。
  2. 秘密情報の提供者と使用者を決める。
  3. enclaveの境界を決める。
  4. enclaveのためのアプリケーションコンポーネントを仕立てる。

アプリケーションの秘密情報を決める

Intel SGXを用いたプリケーションを設計する最初のステップは、アプリケーションの秘密情報を決め絵うことである。

秘密情報は、他人に知られてはならない情報というだけの意味ではない。秘密情報にアクセスすることを目的とするユーザもしくはアプリケーションのみが参照することができ、特権レベルに関係なく他人もしくは他のアプリケーションに秘密情報が開示されるべきではない。秘密情報として可能性のあるものは金融情報、医療記録、個人ID、IDデータ、ライセンスメディアのコンテンツ、パスワード、暗号鍵などを含む。

Tutorial Password Managerでは、以下の表1に示すようなアイテムを秘密情報として即時に識別可能なものである(xxx)。

  • 秘密情報
    • ユーザのアカウントパスワード
    • ユーザのアカウントログイン名
    • ユーザのマスタパスワードおよびパスフレーズ
    • パスワード金庫のためのマスタ鍵
    • アカウントデータベースの暗号鍵

上記の表を拡張して、ログインだけでなくすべてのユーザのアカウント情報を含めることでこのリストを拡張する。修正されたリストを表2に示す。

  • 秘密情報
    • ユーザのアカウントパスワード
    • ユーザのアカウントログイン情報
    • ユーザのマスタパスワードおよびパスフレーズ
    • パスワード金庫のためのマスタ鍵
    • アカウントデータベースの暗号鍵

パスワードが漏れなくてもサービスの名前やURLなどのアカウントの情報は攻撃者にとって有用な情報である。パスワードマネージャ内のデータが漏洩し悪用される恐れがある。漏洩したデータはサービスそのものの攻撃に使用されるかもしれないし、ターゲットが決まっているため、アカウントを乗っ取るためのソーシャルエンジニアリングやパスワードのリセット攻撃に使用されるかもしれない。

アプリケーションの秘密情報の提供者と使用者を決める

アプリケーションの秘密情報を決めると、次のステップは情報の出元と出先を決めす。

現在のIntel SGXのバージョンでは enclaveのコードは暗号化されておらず、つまりアプリケーションのファイルにアクセスできる任意のユーザはそのファイルをディスアセンブリし検査することができる。ファイルが検査されてしまうと何も秘密にすることができず、つまり秘密情報はenclaveのコードに静的にコンパイルしてはならないということを意味する。アプリケーションの秘密情報はenclaveの外部に配置しなければならず、ランタイム時にロードされるものとする。Intel SGXの用語では、enclaveに秘密情報をプロビジョニングすると言う。

秘密情報がTrusted Compute Base(TCB)コンポーネントの外側で作成される場合、Untrustedなコードに対して秘密情報を最小限に提供することが重要である。(Remote AttestationがIntel SGXにとって有用なコンポーネントである理由は、Remote Attestationはサービスの提供者がIntel SGXアプリケーションとの信頼される関係を構築することが許されることと、クライアントシステム上の信頼されたenclaveに対して暗号化された秘密情報をプロビジョニングするために使用される暗号鍵を入手することができるxxx)。秘密情報がenclaveよりも外に持ち出される時も、同様のケアを行わなければならない。一般的なルールとして、アプリケーションの秘密情報は、enclave内で暗号化されずに信頼されないコードに送るべきではない。

残念なことに、Tutorial Password Managerアプリケーションでは、秘密情報をenclaveよりも外に転送する必要があり、これらの秘密情報はいくつかのポイントで明確なテキストとして存在する。エンドユーザはアカウントの情報やパスワードをキーボードもしくはタッチスクリーン越しに入力し、必要ならばWindowsクリップボードにコピーする。パスワードマネージャにとって、利便性を上げるために必要なコア機能である。

つまり、すべての攻撃海面を完全に除去することは不可能だ:ただし、最小化することはでき、秘密情報をenclaveの外側に取り出してプレインテキストで扱う場合の戦略を作成する必要がある。

秘密情報 Source Destination
ユーザのアカウントパスワード ユーザの入力*・パスワード金庫ファイル ユーザインタフェース*・クリップボード*・パスワード金庫ファイル
ユーザのアカウント情報 ユーザの入力*・パスワード金庫ファイル ユーザインタフェース*・パスワード金庫ファイル*
ユーザのマスタパスワード・パスフレーズ ユーザの入力 鍵生成関数
マスター鍵パスワード金庫 鍵生成関数 データベース鍵暗号
パスワードデータベースのための暗号鍵 ランダム生成・パスワード金庫 パスワード金庫crypto・パスワード金庫ファイル

表3. アプリケーションの秘密情報についてsourceとdestination潜在的なリスクはアスタリスク(*)で表示してある。

表3では、Tutorial Password Managerの秘密情報のsourceとdestinationを示している。潜在的な問題ー秘密情報が、信頼されていないコードにより漏洩してしまう可能性のある領域についてはアスタリスク(*)で示している。

Enclaveの境界を決める

秘密情報を決めると、enclaveの境界を決める時間である。アプリケーションで取り扱われる秘密情報のデータフローを作成してみよう。enclaveの境界は、以下の条件を満たす必要がある。

  • アプリケーションの秘密情報を操作するクリティカルなコンポーネントを含んでいなければならない。
  • 機能を実現するための多くの秘密情報を完全に含んでいなければならない。
  • 信頼されていないコードとの相互作用や依存関係は、最小限にしなければならない。

Tutorial Password Managerのデータフローと選択したenclaveの境界を図1に示す。

f:id:msyksphinz:20180823210140p:plain
図1. Tutorial Password Manager内の秘密情報のデータフロー

この図では、秘密情報は円で表現されており、青色の円はプレーンテキスト(暗号化されていない)での秘密情報、緑色の円はアプリケーションにより暗号化された秘密情報を示す。enclaveの境界は暗号化と複合化のルーチンで囲まれており、鍵生成関数(key derivation function:KDF)と乱数生成期が用意されている。

  1. データベース・金庫鍵は、アプリケーションの秘密情報(アカウント情報とパスワード)を暗号化するときに使用し、enclaveの内部で生成されクリアテキストとしてenclaveの外に流れることは絶対にない。
  2. マスター鍵はenclaveの中でユーザのパスフレーズから生成され、データベース・金庫鍵の暗号化と複合化に使用される。マスター鍵は短命で、どのような形でもenclaveの外に漏れることは絶対にない。
  3. データベース・金庫鍵・アカウント情報・アカウントパスワードはenclaveの内部で暗号鍵を使って暗号化され、信頼されないコード上で見えることはない(#1と#2を参照のこと)

残念なことに、暗号化されていない秘密情報をenclaveの境界を越えてやり取りすることは、シンプルり避けられないという問題が生じる。Tutorial Password Managerの実行では、ユーザはパスワードをキーボードから入力したり、パスワードをWindowsクリップボードにコピーしたり留守。enclaveの内部で管理することのできないセキュアでないチャネルが存在し、アプリケーションの機能としてそれは必要不可欠である。このこと自体が非常に大きな問題でありアプリケーションを管理されたコードベースのトップでビルドする。

秘密情報をenclaveの外部で保護する

enclaveの外部で暗号化されていない秘密情報を保護する完全な方法は存在しないが、攻撃する境界を削減するための緩和手法は存在する。簡単に読み取ることのできるフォーム上に情報が存在する時間を可能な限り削減するのがベストである。

秘密情報を、信頼されていないコード上で取り扱う時の一般的なアドバイスを以下に示す。

  • 処理を完了したときに、データバッファを0で埋める。SecureZeroMemory(Windows)のような関数やmemzero_explicit(Linux)などの関数を使って、コンパイラに最適化されないことを保証させる。
  • 秘密情報を格納するためにC++の標準的なtemplate library(STL)コンテナを使用しない。STLコンテナは独自のメモリ管理を行っており、オブジェクトに割り当てられたメモリが、そのオブジェクトが買いオフされたときに安全に解放されていることを保証することが難しい(いくつかのコンテナに対して、この問題を解決するためにカスタムアロケータを使用することはできる)。
  • .NETや、自動メモリ管理の機能を持っているような言語を使用するときは、セキュアなデータを保持するために用意されている型を明示的に仕様sる。他の情方タイプは、がーべじコレクタやジャストインタイムコンパイルの慈悲があり、必要に応じて明確にクリアされたり解放されない場合がある。
  • データをクリップボードに置いた場合、クリップボードを短い時間でクリアするべきである。時にアプリケーションが終了するときに、クリップボードの内容を消去すべきである。

Tutorial Password Managerプロジェクトでは、ネイティブなコードとマネージドコードを使用する。ネイティブなコードでは、wchar_tcharのバッファに割り当て、SecureZeroMemory関数を使用して、バッファの解放前にメモリをクリアする。マネージドなコードでは、.NETのSecureStringクラスを使用する。

SecureStringをマネージドではないコードに渡すためには、System::Runtime::InteropServicesを使用する。

01
using namespace System::Runtime::InteropServices;
LPWSTR PasswordManagerCore::M_SecureString_to_LPWSTR(SecureString ^ss)
{
    IntPtr wsp= IntPtr::Zero;
 
    if (!ss) return NULL;

    wsp = Marshal::SecureStringToGlobalAllocUnicode(ss);
    return (wchar_t *) wsp.ToPointer();
}

ネイティブなコードからマネージドなコードに変換するためには2つの手法がある。SecureStringなオブジェクトが既に存在している場合は、ClearAppendCharメソッドを使ってwchar_tの文字列を格納する。

password->Clear();
for (int i = 0; i < wpass_len; ++i) password->AppendChar(wpass[i]);

SecureString小部栄久とを作成する場合は、SecureStringクラスのコンストラクタを使用してwchar_tの文字列を格納する。

try {
    name = gcnew SecureString(wname, (int) wcslen(wname));
    login = gcnew SecureString(wlogin, (int) wcslen(wlogin));
    url = gcnew SecureString(wurl, (int) wcslen(wurl));
}
catch (...) {
    rv = NL_STATUS_ALLOC;
}

私たちのパスワードマネージは、Windowsクリップボードにパスワードを転送することもサポートしている。クリップボードはインセキュアな格納スペースであり、他のユーザにアクセスされる可能性もあることからMicrosoftクリップボードに秘密情報を格納すべきではないと言っている。パスワードマネージのポイントは、ユーザが強力なパスワード作成して覚えることができないようにすることである。長くてランダムな文字列で構成されたパスワードを生成して、ユーザが手で入力することが不可能なものを作るのである。クリップボードを使用しなければこれを使うことは難しく、リスクをとるほかない。

このリスクを緩和するためには、いくつかの他の注意事項を設定する。1つ目はアプリケーションが終了するときにクリップボードもクリアすることである。これは私たちのネイティブオブジェクトをクリア宇するデストラクタに以下のように記述することで達成できる。

PasswordManagerCoreNative::~PasswordManagerCoreNative(void)
{
    if (!OpenClipboard(NULL)) return;
    EmptyClipboard();
    CloseClipboard();
}

もう一つ、クリップボードのタイマを設ける。パスワードがクリップボードにコピーされると、15ぼ用達とクリップボードの中身がクリアされるような機能を追加する。タイマが既に動作している場合は、古いパスワードがクリアされる前に、新しいパスワードが配置されたということなので、タイマをキャンセルし再起動する。

void PasswordManagerCoreNative::start_clipboard_timer()
{
    // Use the default Timer Queue

    // Stop any existing timer
    if (timer != NULL) DeleteTimerQueueTimer(NULL, timer, NULL);

    // Start a new timer
    if (!CreateTimerQueueTimer(&timer, NULL, (WAITORTIMERCALLBACK)clear_clipboard_proc,
        NULL, CLIPBOARD_CLEAR_SECS * 1000, 0, 0)) return;
}

static void CALLBACK clear_clipboard_proc(PVOID param, BOOLEAN fired)
{
    if (!OpenClipboard(NULL)) return;
    EmptyClipboard();
    CloseClipboard();
}

enclaveのためのアプリケーションコンポーネントを仕立てる

enclave境界と秘密情報について決まったら、enclaveを活用してアプリケーションを構築する時間だ。enclaveの中で行えることには重大な制限があり、これらの制限はenclaveの外部に委任数することになり、既存のアプリケーションを移植する際に、アプリケーションを2つに分割する必要も生じる。

Tutorial Password Managerの最も大きな制約は、enclaveはどのようなI/O操作も実行できないということである。enclaveはキーボードからの読み込みもディスプレイへの表示も行うことができず、すべての秘密情報ーパスワードやアカウント情報ーはenclaveの境界で取得されていなければならない。金庫ファイルからの読み書きも許されない: 金庫ファイルを読み出すコンポーネントは物理I/Oを操作するコンポーネントとは分離しなければならない。したがって、私たちはenclaveの境界での秘密情報の操作だけでなくファイルの操作についても考慮する必要がある。

f:id:msyksphinz:20180823210208p:plain

図2. はアプリケーション子あの基本的なクラス図を示している(ユーザインタフェースは省略)。ここには秘密情報を処理するためのsourceとdestinationの役割も示されている。PasswordManagerCoreクラスは秘密情報のsourceとdestinationであり、ダイアグラム中のGUIとの相互やり取りの機能も含んでいる。表4. は各クラスの役割と目的を示している。

クラス 機能
PasswordManagerCore マネージド C#グラフィカルインタフェース(GUI)とデータの整列・ネイティブレイヤとのインタラク
PasswordManagerCoreNative ネイティブ・Untrusted PasswordManagerCoreクラスとのやりとり。Unicodeとマルチバイト文字データとの変換も担当する(Part-4でより詳細を議論する)
VaultFile マネージド 金庫ファイルの読み書きを行う
Vault ネイティブ・enclave パスワード金庫データをAccountRecordメンバに格納する。読み込み時の金庫ファイルのDeserializeと、書き込み時のReserializeを行う。
AccountRecord ネイティブ・enclave 各アカウントにおけるアカウント情報とパスワードを、ユーザのパスワード金庫に格納する
Crypto ネイティブ・enclave 暗号化関数を実行する
DRNG ネイティブ・enclave ランダム生成のインタフェース

表4. クラスの説明

金庫ファイルを2つに分割しなければならないことに注意する: 1つは物理I/Oの処理、もう一つはその内容を読み込んで処理する。Vaultオブジェクトに対するシリアライズとデシリアライズの関数を追加しなければならない。VaultFileクラスはvaultファイルそのものの構成を知らないため、従ってenclave中に配置されている暗号化関数が必要である。

PasswordManagerCoreNativeクラスとVaultクラスに対して点線を引いている。Part-2で示したように、enclaveはC関数にのみリンクされる。ここでは2つのC++クラスが存在しているが、直接互いにやり取りすることができない: したがってBridge関数を使用して相互にやり取りを行う必要がある。

Intel® Software Guard Extensionコードパス

図2はIntel SGX用のコードパスである。Vaultクラスはenclave内であるため、PasswordManagerCoreNativeクラスはVaultクラスと直接リンクすることはできない。非Intel SGXコードパスはこの関数を使用できないため、Microsoft Cryptograpy API: Next Generation(CNG)を使用する。つまり、Cryptoクラスの2つのコードを管理しなくてはならないことを意味する: 1つはenclave内に存在するものと、もう一つは信頼できない領域に存在しているものである(Part-5では、同様の処理をもう一つ別のクラスで実施しなければならない)。

AWS上で動作するRISC-VチップFireSimのチュートリアルを試す 9. FireSim v1.3.1 でBOOMを動かす

f:id:msyksphinz:20180822232427p:plain

fires.im

FireSim v1.3.1 ではBOOMに対応しているので、さっそくベンチマークプログラムを動かしてみた。

FireSim v1.3.1のダウンロード

$ git clone https://github.com/firesim/firesim.git -b 1.3.1 --recurse-submodules firesim_v131
$ cd firesim_v131
$ ./build-setup.sh  # ツールチェインのビルドでかなり時間がかかる。

FireSim v1.3.1 でRocket-Chipを動かす

FireSim v1.3.1 でRocket-Chipを動かすのは、特に何も設定は必要ない。

$ cd firesim_v131/sim
$ make DESIGN=FireSimNoNIC

FireSim v1.3.1 の Rocket-Chip でベンチマークプログラムを動作させる

ベンチマークプログラムを動作させるためには、以下のコマンドを入力する。

$ make DESIGN=FireSimNoNIC run-bmark-tests
...
  [ PASSED ] /var/masayuki_work/firesim_v131/sim/output/f1/FireSimNoNIC-FireSimRocketChipConfig-FireSimConfig/mm.riscv.out       after 384360 cycles

  [ PASSED ] /var/masayuki_work/firesim_v131/sim/output/f1/FireSimNoNIC-FireSimRocketChipConfig-FireSimConfig/spmv.riscv.out     after 174557 cycles

  [ PASSED ] /var/masayuki_work/firesim_v131/sim/output/f1/FireSimNoNIC-FireSimRocketChipConfig-FireSimConfig/mt-vvadd.riscv.out         after 140509 cycles

  [ PASSED ] /var/masayuki_work/firesim_v131/sim/output/f1/FireSimNoNIC-FireSimRocketChipConfig-FireSimConfig/median.riscv.out   after 24797 cycles

  [ PASSED ] /var/masayuki_work/firesim_v131/sim/output/f1/FireSimNoNIC-FireSimRocketChipConfig-FireSimConfig/multiply.riscv.out         after 62813 cycles

  [ PASSED ] /var/masayuki_work/firesim_v131/sim/output/f1/FireSimNoNIC-FireSimRocketChipConfig-FireSimConfig/qsort.riscv.out    after 362333 cycles

  [ PASSED ] /var/masayuki_work/firesim_v131/sim/output/f1/FireSimNoNIC-FireSimRocketChipConfig-FireSimConfig/towers.riscv.out   after 22493 cycles

  [ PASSED ] /var/masayuki_work/firesim_v131/sim/output/f1/FireSimNoNIC-FireSimRocketChipConfig-FireSimConfig/vvadd.riscv.out    after 17885 cycles

  [ PASSED ] /var/masayuki_work/firesim_v131/sim/output/f1/FireSimNoNIC-FireSimRocketChipConfig-FireSimConfig/dhrystone.riscv.out        after 241757 cycles

  [ PASSED ] /var/masayuki_work/firesim_v131/sim/output/f1/FireSimNoNIC-FireSimRocketChipConfig-FireSimConfig/mt-matmul.riscv.out        after 57309 cycles

最後までベンチマークプログラムが動作した。

FireSim v1.3.1 でBOOMを動作させる

FireSim v1.3.1 でBOOMを動かすためには、DESIGN変数とTARGET_CONFIG変数を変更する。

$ cd firesim_v131/sim
$ DESIGN=FireBoomNoNIC TARGET_CONFIG=FireSimBoomConfig

FireSim v1.3.1 の BOOM でベンチマークプログラムを動作させる

ベンチマークプログラムを動作させるためには、以下のコマンドを入力する。

$ make DESIGN=FireBoomNoNIC TARGET_CONFIG=FireSimBoomConfig run-bmark-tests
...
  [ PASSED ] /var/masayuki_work/firesim_v131/sim/output/f1/FireBoomNoNIC-FireSimBoomConfig-FireSimConfig/mm.riscv.out    after 414688 cycles

  [ PASSED ] /var/masayuki_work/firesim_v131/sim/output/f1/FireBoomNoNIC-FireSimBoomConfig-FireSimConfig/spmv.riscv.out          after 162016 cycles

  [ PASSED ] /var/masayuki_work/firesim_v131/sim/output/f1/FireBoomNoNIC-FireSimBoomConfig-FireSimConfig/mt-vvadd.riscv.out      after 123744 cycles

  [ PASSED ] /var/masayuki_work/firesim_v131/sim/output/f1/FireBoomNoNIC-FireSimBoomConfig-FireSimConfig/median.riscv.out        after 43360 cycles

  [ PASSED ] /var/masayuki_work/firesim_v131/sim/output/f1/FireBoomNoNIC-FireSimBoomConfig-FireSimConfig/multiply.riscv.out      after 80224 cycles

  [ PASSED ] /var/masayuki_work/firesim_v131/sim/output/f1/FireBoomNoNIC-FireSimBoomConfig-FireSimConfig/qsort.riscv.out         after 451168 cycles

  [ PASSED ] /var/masayuki_work/firesim_v131/sim/output/f1/FireBoomNoNIC-FireSimBoomConfig-FireSimConfig/towers.riscv.out        after 31840 cycles

  [ PASSED ] /var/masayuki_work/firesim_v131/sim/output/f1/FireBoomNoNIC-FireSimBoomConfig-FireSimConfig/vvadd.riscv.out         after 26080 cycles

  [ PASSED ] /var/masayuki_work/firesim_v131/sim/output/f1/FireBoomNoNIC-FireSimBoomConfig-FireSimConfig/dhrystone.riscv.out     after 404320 cycles

  [ PASSED ] /var/masayuki_work/firesim_v131/sim/output/f1/FireBoomNoNIC-FireSimBoomConfig-FireSimConfig/mt-matmul.riscv.out     after 55264 cycles

こちらも、最後までベンチマークプログラムが動作した。

Intel Software Guard Extensionのチュートリアルを読む(2. Application Design)

CPU脆弱性についての論文を読んでいると必ずと言っていいほど出てくるIntel SGX(Software Guard Extension)。 なんとなく秘匿データを扱うためのIntelのハードウェア機構なんだろうなと思っていたけど、あまり知ったかぶりをしているのもよくないのでチュートリアルを読んでみることにした。

前回に続いて、今回はTutorialのPart-2だ。今回は前提条件が長々と書いてあるのであまり面白くない。途中までまとめていたが止めてしまった。速く手を動かしたい。

software.intel.com


Intel® Software Guard Extension チュートリアル連載

Part-2 アプリケーションデザイン

Intel®Software Guard Extensions (Intel® SGX)チュートリアルの第2回は、アプリケーションの開発についてである: シンプルなパスワードマネージャについて取り上げる。アプリケーションを一から開発するため、Intel SGXについて最初から弁学ぶためにはよい教材である。Intel SGXデザインと、他に影響を与えるアプリケーションアーキテクチャについて学ぶ。

最初に、本連載の最初のチュートリアルを読むか、Intel® Software Guard Extensions Tutorial Series のすべての連載のリストを見ておくこと。

一目でわかるパスワードマネージャ

多くの人々はパスワードマネージャの役割について知っているが、アプリケーションの設計をする前にその基本的な機能について見ておくことは良いことだろう。

パスワードマネージャの主たる目的は

  • エンドユーザが記憶する必要のあるパスワードの数を減らす。
  • エンドユーザが通常選択しがちなパスワードよりもより強固なパスワードを使用できるようにする。
  • すべてのアカウントで異なるパスワードを使えるような環境を整える。

パスワードの管理は、インターネットユーザにとって頭を抱える問題になっている。いくつかの研究により、パスワード管理の問題が指摘されている。Microsoftの2007年の研究ーほぼ10年前ーによると、一人当たり平均25個のアカウントを持っていると推定されている。さらに最近、2014年になると、DashlaneはUSユーザが平均で130個のアカウントを持っていると推定されており、全世界では平均で90個のアカウントを持っているという研究成果が発表された。さらに問題はこれで終わらない。人々は「良い」パスワードを選ぶのが悪く、複数のサイトで同じパスワードを頻繁に再利用する傾向にある。これにより驚くべき攻撃につながっている。この問題は、2つの問題に集約される: ハッキングされにくいパスワードは人々にとっても覚えにくいということ、そしてより多くの種類のパスワードと、ペアとなるアカウントを記憶するというのは問題を複雑にするということである。

パスワードマネージャを使用すると、ユーザは1つの非常に強力なパスフレーズを記憶しておくだけで、パスワードデータベースや金庫室にアクセスすることができる。一度パスワードマネージャにより認証されると、ユーザはパスワードデータベースに格納しているすべてのパスワードにアクセスでき、必要に応じてパスワードを認証フィールドにコピー&ペーストすることができるようになる。もちろん、鍵となる脆弱性はパスワードデータベースそのものである: このデータベースにはすべてのパスワードが格納されているため、攻撃者にとっては格好の攻撃対象である。したがって、パスワードデータベースは強力な暗号化技術を使って暗号化され、ユーザのマスタパスフレーズによってデータベース内のデータを複合化する仕組みになっている。

チュートリアルの目標は、商用製品と同一の機能を持つシンプルなパスワードマネージャを作り上げる中で、セキュリティ対策の良い例題を取り上げながら、Intel SGXを使用した開発について学ぶことである。チュートリアルのパスワードマネージャを、"Tutorial Password Manager with Intel® Software Guard Extensions"(長くてちょっと発音しにくいが、記述的だ。)と呼ぶことにする。これは商用プロダクトの機能を説明する目的のものではなく、すべてのセーフガードの機能を含むわけではないが、詳細レベルは必要ではない。

基本的なアプリケーションの必要要件

私たちがこれから紹介するIntel SGXを活用するパスワードマネージャは、商用のプロダクトレベルのアプリケーションを作ることではない。Tutorial Password Managerは複数のオペレーティングシステムで動作する必要はないし、すべてのCPUアーキテクチャで動作する必要はない。かなりリーズナブルな条件から始めよう。

私たちの基本的なアプリケーションの要件は以下のとおりである:

必要条件と設計決定

  • Intel SGXが搭載されているかどうかにかかわらずすべてのプラットフォームで動作しなければならない。
  • Microsoft Windows Vista®, 64-bit以降のサポートが必要である。(Intel SGXサポートには Windows® 10, 64-bit以降が必要である)

  • Intel® Data Protection Technology(Intel® DPT)をサポートするインテルのプロセッサが必要である。

  • サードパーティのライブラリやユーティリティに依存してはならない。

最初の条件は奇妙かもしれないが、少なくとも世の中にはIntel SGXを使用していないレガシーな環境も存在する。Intel SGXを使用している環境ではハードな(xxx)実行環境が提供されるが、そうでない環境もまた現役である。パスワードマネージャは、パスワードデータベースをIntel SGXを使用していない、古いシステムでもサポートする必要がある。これは2系統のコードを実装しなければならないときの良い題材である。

2番目の条件は、非Intel SGXコードのパスでいくつかのライブラリを使用する必要があるからである。64-bitの制約は、64-bitの演算の際に型の大きさを気にしなくてもよいということと、暗号化のアルゴリズムにおいて64-bitのコードを使用してパフォーマンスを向上させる目的がある。

3番目の条件は、非Intel SGX環境においてRDRAND命令を使用するための条件である。この条件により、乱数の生成と高品質なエントロピーソースであることを保証できるようになる。RDSEED命令を使用することができるシステムでも同様の保証が得られる(RDRANDおよびRDSEED命令についての情報は、Intel® Digital Random Number Generator Software Implementation Guideを参照のこと)。

4番目の条件は、開発者に必要なソフトウェアのリストを最小限に抑えるためのものである。サードパーティのライブラリ・フレームワーク・アプリケーション・ユーティリティはダウンロードやインストールの必要が無い。しかし、ユーザインタフェースを作成するにあたり、サードパーティフレームワークを利用する必要がある。それらのオプションは、

ユーザインタフェースフレームワーク

f:id:msyksphinz:20180821191302p:plain

必要条件と設計決定

パスワードの金庫の必要条件

必要条件と設計決定

  • パスワード金庫は持ち運び可能(portable)でなければならない。
  • 金庫は暗号化されていなければならない。
  • すべての暗号化処理は認証された暗号化モードを使用しなければならない。

パスワード

必要条件と設計決定

  • アカウントパスワードは暗号化され金庫に格納されていなければならない。
  • アカウントのパスワードはオンデマンドに複合化されなければならない。

暗号化アルゴリズム

必要条件と設計決定

  • 暗号化はAES-GCMを使って行われる。
    • 128-bit鍵
    • 128-bit認証タグ
    • Additional Authenticated Data(AAD)は使用しない
    • 97-bit IV

暗号鍵とユーザ認証

必要条件と設計決定

  • 金庫のカギはRDSEED命令から生成されるか、RDSEED命令をサポートしていないプラットフォームではRDRAND命令から生成されるシードを利用して生成される。
  • 金庫のカギはマスター鍵を使用して暗号化される。
  • マスター鍵はユーザのパスフレーズから、KDFを通じて伝えられる。
  • KDFはSHA-256をベースにしている。

アカウントの詳細

必要条件と設計決定

  • 金庫は以下のアカウント情報を格納する。
    • アカウント・ベンダ名
    • アカウント・ベンダURL
    • ログインID
    • 認証パスワード
  • 金庫は固定された数のアカウントを持っている。
f:id:msyksphinz:20180821191450p:plain