安価な NFC タグで秘密情報を安全に携行する試み
はじめに
最近 NFC まわりの調査と実験を行っています。切り口が多く奥の深い技術ですが、NFC タグ製品について調べている過程で現在もっとも広く出回っている NXP Semiconductors 製の「NTAG21x」シリーズ (ISO/IEC 14443 Type A: MIFARE Ultralight) に実装されている興味深いアクセス制御機構を知りました。
NFC タグの一般的な「手軽さ」とは裏腹に、扱いを誤るとタグがあっさり使用不能になるリスクと背中合わせであるためか今のところこの機構に言及した記事やアプリケーションはあまり見かけません。しかし、仕様を理解した上で適切に取り回せばこの安価なタグ製品の使途を大きく拡げることができるでしょう。
- 媒体が小さく軽薄で嵩張らない
- 非接触型であるため露出の必要がなく忍ばせやすい
- 緊急時には簡単に破壊・破棄できる
手元ではこういった物理的な特長にも注目し、NTAG21x を秘密情報の格納に利用するアイディアを形にしてみました。データ圧縮と複数タグへの分割格納にも対応しています。今回はこの NTAG21x のアクセス制御機構に関する情報の整理を行い、試作した Android アプリケーションの内容を紹介します。
ちなみに手元では実験用に NTAG213 を計 110 枚、NTAG216 を計 25 枚調達しました。どちらもまず Amazon で少量を購入し様子見を経て後顧の憂いなく使い潰せるようにより安く入手できる Aliexpress で買い足しを行いました。参考に購入元と購入時の価格を掲載します。(個人的には現時点で Aliexpress を利用する際には万一の事故に備えての措置をとっています)NTAG213(ユーザメモリ容量 144 バイト)
- サンワサプライ NFCタグ(10枚入り) 白 MM-NFCT
- amazon.co.jp 入数 10:¥837- Free Shipping 100pcs/Lot Ntag213 NFC TAG Sticker...
- aliexpress.com 入数 100:US $11.17NTAG216(ユーザメモリ容量 888 バイト)
なお、本記事においては NTAG メモリ内容の参照に NXP 純正の次のアプリケーションを使用しています。
- タグステッカー, SODIAL(R)5pcsキュートな888バイトスマートNFCタグステッカー
- amazon.co.jp 入数 5:¥408- 5YOA 20pcs NFC Ntag216 888 Bytes Tag Sticker...
- aliexpress.com 入数 20:US $8.99
- NFC TagInfo by NXP - play.google.com
1. NTAG21x のアクセス制御機構とネイティブコマンド
データシート
- NTAG213/215/216 NFC Forum Type 2 Tag - 2 June 2015 Product data sheet (PDF) - www.nxp.com
アクセス制御機構の概要と簡単なデモ
- NTAG21x メモリ上の任意のページ(1 ページ = 4 バイト)以降へのアクセス要求に対し当該タグをパスワードによって保護できる
- パスワードによる保護は Write 要求のみならず Read 要求に対しても設定可能
- パスワード認証連続試行回数の上限値を設定することが可能であり、この回数を超過すると当該タグの認証機能は永久に封鎖されパスワード正否にかかわらず認証通過不能となる
デモ 1: 試作アプリから NTAG213 にデータの書き込みを行い全ページの読み書きをパスワードで保護する (動画 1分39秒)
デモ 2: 上記デモ 1 でのパスワード設定時に併せて設定した認証試行上限回数を超過する操作を行った結果タグが自爆する様子 (動画 1分2秒)
アクセス制御機構の詳細
- データシート中の記事
- "1. General description"
- "8.5 Memory organization"
- "8.6 NFC counter function"
- "8.8 Password verification protection"
- 要点のまとめ
- NTAG213 の EEPROM メモリの総ページ数は 45(0h〜2Ch)。NTAG215 は 135 ページ, NTAG216 は 231 ページであり、いずれも末尾の 4 ページが Configuration Pages である。下図の NTAG213 のメモリレイアウトを参照のこと。なお 1 ページ = 4 バイト
- Configuration 第 1 ページの第 4 バイト「AUTH0」にはパスワード保護対象とする開始ページのアドレスを指定する。このフィールドに実在ページの範囲を超えるアドレスが格納された状態ではパスワード保護は無効。出荷時のデフォルトは 0xFF
- Configuration 第 2 ページの第 1 バイト「ACCESS」の PROT ビットが 0 ならパスワード保護の対象は Write 要求のみ。同ビットが 1 なら Read, Write 要求の両方が対象となる
- パスワード(PWD)は 4バイト固定。Configuration 第 3 ページへ格納。出荷時の PWD のデフォルトは 0xFFFFFFFF
- PWD は可読文字列でなくても許容されるためバリエーションは 4 バイトで 256^4 であり用途によっては意図的に非 ASCII データを適用する選択もあり得るだろう
- 「ACCESS」の AUTHLIM にはブルートフォース攻撃対策用にパスワード認証要求(PWD_AUTH:後出)試行回数上限を設定可能。認証連続失敗回数がこの値を超えると以降の PWD_AUTH はパスワードの正否にかかわらず永久に無効となる。試行可能回数の範囲で PWD_AUTH が成功すると失敗回数を保持する内部カウンタはリセットされる。AUTHLIM の初期値は当該機能の無効を示す 000b
- Configuration 第 4 ページの先頭 16 ビットの Password Acknowledge(PACK)はパスワード認証通過時の肯定応答値。デフォルトは 0x0000
- パスワード保護が有効なメモリ領域の読み書きはパスワード認証を通過し PACK を得た後に可能となる
- PWD および PACK の読み出しは不可能。両フィールドへの READ 要求に対し NTAG は常に 0x00 を返す
- NTAG21x のパスワード保護機構は不正なメモリアクセスの防止に特化したものでありデータ暗号化等はアプリケーション側の要件
- Configuration Pages を永続的にロックする CFGLCK 機構にも要注意
- フィールドの整理
(※データシートから引用した図・表を説明のために再構成しています)
- (参考)NTAG213 初期状態のメモリダンプ:ページ 29h 以降が Configuration Pages
※ NXP 提供の Android アプリ TagInfo での表示
関連する NTAG コマンド
上記アクセス制御機構を利用する上で最低限必要な NTAG21x ネイティブコマンドおよび関連情報を示す。
- データシート中の記事
- "9. Command overview"
- 要点のまとめ
- NTAG コマンドに対する最短のレスポンスは 4ビットの ACK or NACK である
- Ah: Acknowledge (ACK)
- 0h: NAK for invalid argument (i.e. invalid page address)
- 1h: NAK for parity or CRC error
- 4h: NAK for invalid authentication counter overflow
- 5h: NAK for EEPROM write error
- コマンドに付与する CRC 値は ISO/IEC 14443 の規約に準拠のこと (実装例)
- GET_VERSION コマンド
NTAG 製品のバージョン,ストレージサイズ等を得る。Configuration Pages のアドレス取得に必要- リクエスト
- コマンド 1 バイト(60h) + CRC 2 バイト
- レスポンス
- OK: データ 8 バイト+ CRC 2 バイト
- NG: NAK 各値
NTAG 21x GET_VERSION レスポンスデータの内容 Byte no. Description NTAG213 NTAG215 NTAG216 Interpretation 0 fixed Header 00h 00h 00h 1 vendor ID 04h 04h 04h NXP Semiconductors 2 product type 04h 04h 04h NTAG 3 product subtype 02h 02h 02h 50 pF 4 major product version 01h 01h 01h 1 5 minor product version 00h 00h 00h V0 6 storage size 0Fh 11h 13h see following information 7 protocol type 03h 03h 03h ISO/IEC 14443-3 compliant
NTAG 製品の識別方法
storage size の上位 7ビットを符号なし整数値 n として解釈する。使用可能なユーザーメモリの合計サイズは、最下位ビットが 0 の場合には 2^n であり最下位ビットが 1 の場合には 2^n 〜 2^(n+1) である。これを識別に利用する。
NTAG213 での GET_VERSION レスポンス実例
HEX: 00 04 04 02 01 00 0F 03
--> 0x0F = BIN: 00001111 --> BIN: 0000111 = 0x07 --> 2^7 = 128, 2^(7+1) = 256
※ 128〜256 バイトのユーザーメモリ空間を持つ製品は NTAG213(144 バイト)
NTAG216 での GET_VERSION レスポンス実例
HEX: 00 04 04 02 01 00 13 03
--> 0x13 = BIN: 00010011 --> BIN: 0001001 = 0x09 --> 2^9 = 512, 2^(9+1) = 1024
※ 512〜1024 バイトのユーザーメモリ空間を持つ製品は NTAG216(888 バイト)
- リクエスト
- READ コマンド
所定のページアドレスから始まる 4 ページ分(16 バイト)のデータを得る- リクエスト
- コマンド 1 バイト(30h)+ 開始アドレス 1 バイト + CRC 2 バイト
- レスポンス
- OK: データ 16 バイト+ CRC 2 バイト
- NG: NAK 各値
- リクエスト
- WRITE コマンド
所定のページアドレスへ 1 ページ分(4 バイト)のデータを書き込む- リクエスト
- コマンド 1バイト(A2h)+ 対象アドレス 1 バイト + データ 4 バイト + CRC 2 バイト
- レスポンス
- OK: ACK
- NG: NAK 各値
- リクエスト
- PWD_AUTH コマンド
パスワードで保護された領域へのアクセス要求に先行してパスワード認証を要求する。ここで肯定応答である PACK が得られれば所定のアクセスが可能となる(PACK の値はデフォルト 0x0000 。変更可)- リクエスト
- コマンド 1 バイト(1Bh)+ パスワード 4 バイト + CRC 2 バイト
- レスポンス
- OK: PACK 2 バイト
- NG: NAK 各値
- リクエスト
- NTAG コマンドに対する最短のレスポンスは 4ビットの ACK or NACK である
2. Android API による NTAG21x のハンドリングについて
手順等
public final class NfcA
Provides access to NFC-A (ISO 14443-3A) properties and I/O operations on a Tag.
Acquire a NfcA object using get(Tag).
The primary NFC-A I/O operation is transceive(byte[]). Applications must implement their own protocol stack on top of transceive(byte[]).
NfcA get (Tag tag)
Get an instance of NfcA for the given tag.
Returns null if NfcA was not enumerated in getTechList(). This indicates the tag does not support NFC-A.
Parameters
Returns
byte[] transceive (byte[] data)
Get an instance of NfcA for the given tag.
Send raw NFC-A commands to the tag and receive the response.
Applications must not append the EoD (CRC) to the payload, it will be automatically calculated.
Applications must only send commands that are complete bytes, for example a SENS_REQ is not possible (these are used to manage tag polling and initialization).
Use getMaxTransceiveLength() to retrieve the maximum number of bytes that can be sent with transceive(byte[]).
This is an I/O operation and will block until complete. It must not be called from the main application thread. A blocked call will be canceled with IOException if close() is called from another thread.
Parameters
Returns
Throws
コードを書いて試す
- パスワードを設定する(write 要求からの保護)
- NTAG21x 種別を確認
- パスワードとして "0000" を設定
- ページ 04h 以降を保護対象とする
: if (isNfcA) { int ntagMaxPage = -1, ntagConfPage0; NfcA nfca = NfcA.get(tag); try { nfca.connect(); } catch (IOException e) { Log.d(TAG, "NfcA.connect() err: " + e.toString()); return; } // NTAG 種別取得 try { byte[] res = nfca.transceive(new byte[]{ (byte) 0x60, // GET_VERSION }); Log.d(TAG, "GET_VERSION reslen=" + res.length + " res=" + bytesToHexString(res)); if (res.length == 8) { if (res[0] == 0x00 && res[1] == 0x04 && res[2] == 0x04 && res[3] == 0x02) { byte ntagStorageSize = res[6]; if (ntagStorageSize == 0x0F) { ntagMaxPage = 45; // 0x2D = NTAG213 } else if (ntagStorageSize == 0x11) { ntagMaxPage = 135; // 0x87 = NTAG215 } else if (ntagStorageSize == 0x13) { ntagMaxPage = 231; // 0xE7 = NTAG216 } } } } catch (IOException e) { Log.d(TAG, "GET_VERSION err:" + e.toString()); } if (ntagMaxPage == -1) { Log.d(TAG, "is not NTAG21x"); if (nfca.isConnected()) { try { nfca.close(); } catch (IOException e) { Log.d(TAG, "NfcA.close() err: " + e.toString()); } } return; } // Congiguration Pages 開始位置 ntagConfPage0 = ntagMaxPage - 4; try { // パスワードを設定 int page = ntagConfPage0 + 2; // config page 2 byte[] res = nfca.transceive( new byte[]{ (byte) 0xA2, // WRITE (byte) page, '0', '0', '0', '0' } ); Log.d(TAG, "WRITE p" + page + " reslen=" + res.length + " res=" + bytesToHexString(res)); // 保護開始ページを設定 page = ntagConfPage0; // config page 0 res = nfca.transceive( new byte[]{ (byte) 0xA2, // WRITE (byte) page, (byte) 0x04, (byte) 0x00, (byte) 0x00, (byte) 0x04 // 保護開始ページ } ); Log.d(TAG, "WRITE p" + page + " reslen=" + res.length + " res=" + bytesToHexString(res)); } catch (IOException e) { Log.d(TAG, "WRITE err:" + e.toString()); } if (nfca.isConnected()) { try { nfca.close(); } catch (IOException e) { e.printStackTrace(); } } } :
- 上のコードにより NTAG213 のページ 04h 以降への書き込みにパスワード認証通過が必須となった状況
AUTH0 に保護開始ページ指定
PROT ビットはデフォルトの 0 のまま
- パスワードを設定する(read / write 要求からの保護)
- NTAG21x 種別を確認
- パスワードとして "0000" を設定
- ページ 04h 以降を保護対象とする
- PROT ビットを立て read 要求からの保護も有効に
: if (isNfcA) { int ntagMaxPage = -1, ntagConfPage0; NfcA nfca = NfcA.get(tag); try { nfca.connect(); } catch (IOException e) { Log.d(TAG, "NfcA.connect() err: " + e.toString()); return; } // NTAG 種別取得 try { byte[] res = nfca.transceive(new byte[]{ (byte) 0x60, // GET_VERSION }); Log.d(TAG, "GET_VERSION reslen=" + res.length + " res=" + bytesToHexString(res)); if (res.length == 8) { if (res[0] == 0x00 && res[1] == 0x04 && res[2] == 0x04 && res[3] == 0x02) { byte ntagStorageSize = res[6]; if (ntagStorageSize == 0x0F) { ntagMaxPage = 45; // 0x2D = NTAG213 } else if (ntagStorageSize == 0x11) { ntagMaxPage = 135; // 0x87 = NTAG215 } else if (ntagStorageSize == 0x13) { ntagMaxPage = 231; // 0xE7 = NTAG216 } } } } catch (IOException e) { Log.d(TAG, "GET_VERSION err:" + e.toString()); } if (ntagMaxPage == -1) { Log.d(TAG, "is not NTAG21x"); if (nfca.isConnected()) { try { nfca.close(); } catch (IOException e) { Log.d(TAG, "NfcA.close() err: " + e.toString()); } } return; } // Congiguration Pages 開始位置 ntagConfPage0 = ntagMaxPage - 4; try { // パスワードを設定 int page = ntagConfPage0 + 2; // config page 2 byte[] res = nfca.transceive( new byte[]{ (byte) 0xA2, // WRITE (byte) page, '0', '0', '0', '0' } ); Log.d(TAG, "WRITE p" + page + " reslen=" + res.length + " res=" + bytesToHexString(res)); // 保護開始ページを設定 page = ntagConfPage0; // config page 0 res = nfca.transceive( new byte[]{ (byte) 0xA2, // WRITE (byte) page, (byte) 0x04, (byte) 0x00, (byte) 0x00, (byte) 0x04 // 保護開始ページ } ); Log.d(TAG, "WRITE p" + page + " reslen=" + res.length + " res=" + bytesToHexString(res)); // read 要求からの保護も有効に page = ntagConfPage0 + 1; // config page 1 res = nfca.transceive( new byte[]{ (byte) 0xA2, // WRITE (byte) page, (byte) 0x80, // read,write protect (byte) 0x05, (byte) 0x00, (byte) 0x00 } ); Log.d(TAG, "WRITE p" + page + " reslen=" + res.length + " res=" + bytesToHexString(res)); } catch (IOException e) { Log.d(TAG, "WRITE err:" + e.toString()); } if (nfca.isConnected()) { try { nfca.close(); } catch (IOException e) { e.printStackTrace(); } } } :
- 上のコードにより NTAG213 のページ 04h 以降の読み書きにパスワード認証通過が必須となった状況
- パスワードを解除する
- NTAG21x 種別を確認
- PWD_AUTH コマンドでパスワード "0000" を提示し認証要求
- AUTH0 に有効範囲外のページ番号 FFh を書き込むことでパスワード保護を無効化する
: if (isNfcA) { int ntagMaxPage = -1, ntagConfPage0; NfcA nfca = NfcA.get(tag); try { nfca.connect(); } catch (IOException e) { Log.d(TAG, "NfcA.connect() err: " + e.toString()); return; } // NTAG 種別取得 try { byte[] res = nfca.transceive(new byte[]{ (byte) 0x60, // GET_VERSION }); Log.d(TAG, "GET_VERSION reslen=" + res.length + " res=" + bytesToHexString(res)); if (res.length == 8) { if (res[0] == 0x00 && res[1] == 0x04 && res[2] == 0x04 && res[3] == 0x02) { byte ntagStorageSize = res[6]; if (ntagStorageSize == 0x0F) { ntagMaxPage = 45; // 0x2D = NTAG213 } else if (ntagStorageSize == 0x11) { ntagMaxPage = 135; // 0x87 = NTAG215 } else if (ntagStorageSize == 0x13) { ntagMaxPage = 231; // 0xE7 = NTAG216 } } } } catch (IOException e) { Log.d(TAG, "GET_VERSION err:" + e.toString()); } if (ntagMaxPage == -1) { Log.d(TAG, "is not NTAG21x"); if (nfca.isConnected()) { try { nfca.close(); } catch (IOException e) { Log.d(TAG, "NfcA.close() err: " + e.toString()); } } return; } // Congiguration Pages 開始位置 ntagConfPage0 = ntagMaxPage - 4; // パスワード認証 // 通過すれば close するまで権限が持続 boolean authOk = false; try { byte[] res = nfca.transceive( new byte[]{ (byte) 0x1b, // PWD_AUTH '0', '0', '0', '0' } ); Log.d(TAG, "PWD_AUTH reslen=" + res.length + " res=" + bytesToHexString(res)); if (res.length == 2) { authOk = true; } } catch (IOException e) { Log.d(TAG, "PWD_AUTH err:" + e.toString()); } if (authOk) { // パスワード保護を解除 try { int page = ntagConfPage0; // config page 0 byte[] res = nfca.transceive( new byte[]{ (byte) 0xA2, // WRITE (byte) page, (byte) 0x04, (byte) 0x00, (byte) 0x00, (byte) 0xFF // 有効範囲超のページ指定で保護は無効に } ); Log.d(TAG, "WRITE p" + page + " reslen=" + res.length + " res=" + bytesToHexString(res)); } catch (IOException e) { Log.d(TAG, "PWD WRITE err:" + e.toString()); } } if (nfca.isConnected()) { try { nfca.close(); } catch (IOException e) { Log.d(TAG, "NfcA.close() err: " + e.toString()); } } } :
- 上のコードにより前掲 2.のコードによるパスワード保護をこのコードで解除した状況
ここでは PWD 自体には手をつけておらず PROT ビットも 1 のままだが、AUTH0 = FFh につきユーザ領域を自由に読み書き可能な状態に戻っている
NTAG21x では write 要求のみならず read 要求に対してもパスワードによる保護が可能。ただしパスワードは両者共通
3. 試作した Android アプリについて
NTAG で秘密情報を扱うという考え方
ここまで見てきたように NTAG21x はパスワード設定により Write 要求のみならず Read 要求を弾くことも可能です。これは興味深い機能ですが、両者のパスワードが共通であるためたとえば次のような取り回しはできません。
このように複数のロールでタグを共用したい場合ではなく、所定のタグを独占的・排他的に扱いたい場合にこそこの機能は有用でしょう。その典型的な使用例として秘密情報の格納を想起しました。以下の発想によるものです。
なお、こういった使途においては NFC の世界で「データ交換」を行う上での便宜としての NDEF 形式を使用する必然性はありません。ユーザメモリ全体を任意の形式で効率よく取り回せばよいでしょう。
試作中の情報収集時に、秘密情報を紙媒体で管理する「パスワードノート」という製品があることを知りました。
- とにかく書いておきたい。という方に。 - アマノコトワリ舎 自然と共に。パスワード管理ノート新発売! - www.amacoto.com
- 「パスワード忘れた」をノートに書き留めて解消! アナログなのに画期的な「パスワードノート」が話題 - www.sankei.com
ノートはA5サイズ44ページで、パスワードを管理したいサイトごとに1ページを使う。各ページには、六角形の枠が上下に「8」の字形に並んだマス目と、サイトの名前やID、アドレスなどを記入する欄がある。
48あるマス目で「起点」を決め、「右回り」「左回りで2つ飛ばし」など、自分で決めたルールに従ってパスワードを記入。各ページには解読のための「ヒント」を記入する欄があり、自分に分かるようヒントを書く。余ったマス目にカムフラージュ用の英数字を書き込めば、書いた本人以外は解読できない仕組みだ。 :
ストレージやネットワークを使用しない点が共通しており興味を持ちました。たしかに「手書き」には他の方法では決して及ばない様々な柔軟性があります。これがヒントとなって NTAG を手帳へ貼付して使うアイディアに至りました。ふたつの文化の利点を活用できる良い組み合わせだと思います。
実装内容
試作の仕様として以下の内容を想定しました。
NTAG ユーザメモリの使いかた
========= ユーザメモリ領域全体の構成 =========
1. page 04h - 05h に固有の管理情報を格納する
2. page 06h 以降に gzip データを格納する
========= 1. 管理情報について =========
[page 04h]
0 1 2 3
+----+----+----+----+
|'t' |'t' | -- | -- |
+----+----+----+----+
第 1, 2 バイト
識別子 "tt"
第 3, 4 バイト
予備
※当初、データを分割格納したタグセットの識別用領域と
することを想定したが費用対効果に乏しいと判断し中止
[page 05h]
0 1 2 3
+----+----+----+----+
| NN | -- | NN | NN |
+----+----+----+----+
第 1 バイト
最上位ビット:後続タグの有無 0=後続なし 1=後続あり
下位 4 ビット:タグ連番(0h - Fh)
第 2 バイト
予備
第 3, 4 バイト
ページ 06h 以降に格納ずみの gzip データサイズ
short ビッグエンディアン
========= 2. gzip データについて =========
- テキストデータの gzip 圧縮はオンメモリで行う
- タグへ書き込む際には gzip データの半固定ヘッダ
10バイト(*)を除去し、読み出しの際には当該ヘッダを
補填した上で unpack する
(*) http://www.onicos.com/staff/iz/formats/gzip.html
- データを複数のタグへ分割格納する場合は上記の管理情報で
連番管理を行うが、事後に各タグから単独で部分データを
読み出すことも可能とするために、タグへ書き込むのは
「元データ全体を圧縮した gzip データの一部分」ではなく
「元データを適切な位置で分割して圧縮した gzip データ」とする
ソースコード
※本記事の冒頭でも触れたように NTAG21x のアクセス制御機構の扱いには十分な注意が必要です。 試作のソースコード公開はあくまでも技術情報の紹介を目的とするものでありプログラム実行時の動作は保証しません。これを使用して何らかの損害が発生したとしても当方は一切の責任を負いません。
動作の様子
以下のデモ動画ではテストデータとして次の 4,978 バイトの英単語リストを使用。このデータを圧縮して 3 枚の NTAG216 に分割格納しています。
START, ability, abroad,
accept, access, accident,
according, account, action,
activity, actually, add,
addition, additional, address,
adult, advance, advanced,
advantage, advice, advise,
age, agency, agent,
agree, ahead, air,
airline, allow, amount,
angry, announce, announcement,
anxious, appear, appearance,
application, apply, approach,
area, arrange, arrangement,
arrival, arrive, article,
attack, attend, attention,
available, average, avoid,
aware, balance, balanced,
bar, base, basic,
bear, beat, beauty,
benefit, bill, bit,
block, blood, board,
borrow, boss, branch,
break, broad, broadcast,
brush, budget, burn,
business, busy, cab,
cable, call, cancel,
capital, care, careful,
case, cash, catalog,
catch, cause, century,
certain, certainly, chance,
change, charge, chart,
cheap, check, chemical,
choose, citizen, claim,
clear, clerk, close,
clothes, collect, collection,
comfortable, common, communication,
company, compare, complain,
complete, concern, condition,
contact, contain, content,
continue, contract, control,
convenient, conversation, copy,
corner, correct, cost,
count, couple, course,
court, cover, crash,
create, credit, cross,
crowd, crowded, customer,
daily, damage, data,
date, deal, decide,
decision, defence, degree,
delay, deliver, demand,
department, depend, deposit,
describe, design, destroy,
detail, develop, dial,
diet, difference, difficulty,
direct, direction, directly,
director, discount, discuss,
disease, disk, display,
distance, district, document,
double, doubt, downtown,
dress, drive, drop,
drug, due, earn,
earthquake, economy, edge,
education, effect, effective,
effort, electricity, employee,
empty, encourage, energy,
enjoy, enter, entrance,
equipment, event, examination,
example, excellent, except,
exciting, excuse, exercise,
exit, expect, expense,
expensive, experience, expert,
explain, express, extra,
face, fail, fair,
fall, fan, fare,
fashion, fault, favor,
favorite, feature, fee,
fence, figure, file,
fill, film, final,
fine, fire, firm,
fit, fix, flag,
flat, flood, floor,
flow, follow, following,
force, foreign, form,
former, forward, found,
foundation, free, freeze,
front, fund, furniture,
further, future, gain,
garage, gas, gather,
general, global, goal,
government, grade, graduagte,
grand, ground, guard,
guess, guide, handle,
hang, happen, hardly,
head, heart, heat,
highly, hire, hold,
host, human, hurt,
illness, image, immediately,
improve, include, income,
increase, individual, industry,
inform, information, insect,
instead, instruction, insurance,
intend, interest, international,
interview, introduce, invitation,
invite, issue, item,
job, join, judge,
knowledge, lack, land,
language, law, lawyer,
lay, lead, lend,
length, level, license,
lie, lift, likely,
limit, limitation, line,
list, load, loan,
local, location, lock,
lose, loss, luck,
lucky, luggage, mail,
main, major, majority,
manage, management, manager,
mark, market, master,
match, material, matter,
meal, means, measure,
media, medicine, mention,
message, method, mind,
minute, miss, mix,
model, modern, moment,
monthly, movemet, nation,
national, nearly, necessary,
notice, offer, office,
officer, official, operate,
operation, operator, opinion,
order, organization, own,
pack, package, pain,
part, party, pass,
passenger, patient, pay,
payment, per, performance,
performance, period, permit,
personal, pick, plain,
plan, plant, plate,
pleasure, plenty, point,
pole, policy, popular,
position, possible, post,
powerful, practice, prefer,
prepare, present, press,
prevent, price, print,
private, probably, problem,
process, produce, product,
production, professional, profit,
program, progress, project,
promise, property, protect,
protection, proud, provide,
public, publish, purpose,
puzzle, quality, quarter,
race, raise, range,
rapid, rate, reach,
ready, realize, reason,
receive, recent, recently,
recommend, record, reduce,
refer, regard, regarding,
regular, relationship, remember,
remove, rent, repair,
report, request, require,
research, reserve, responsible,
rest, result, return,
rise, room, route,
row, run, rush,
safe, safety, sail,
salary, sale, save,
schedule, seat, section,
security, sense, separate,
separately, serious, serve,
service, shake, shape,
share, ship, shock,
short, show, sign,
signal, single, situation,
skill, skin, slip,
social, solve, sort,
sound, spare, spend,
spot, spread, staff,
stage, stair, stand,
standard, state, statement,
step, stock, store,
storm, stretch, strike,
study, subject, submit,
succeed, success, suffer,
suggest, suit, supply,
support, suppose, sure,
surprise, survey, swing,
system, taste, tax,
temperature, terible, term,
tie, tight, tip,
tired, tool, total,
touch, tour, trade,
traffic, training, trip,
trouble, turn, type,
usual, valuable, value,
view, vote, want,
war, warn, way,
wear, weather, win,
wind, wire, wise,
wonder, worth, worthy,
xenophobia, yard, yarn,
yawn, yearn, yell,
yellow, yesterday, yet,
yield, yolk, young,
youth, zeal, zenith,
zigzag, zinc, zoom,
END
動画:5分1秒
※パスワード認証試行上限回数を超過した状況の動画はこちら
(tanabe)