2017/04/30

「ChaCha20 and Poly1305」は「ChaCha20-Poly1305」ではないだろう

ChaCha20を調べ始めたとき、私はリンク先を載せていた。
それは、RFC-7539だ。

こちらを読んでいたのだが、出てきたのはRFC-7905だった。
新しいTLSの暗号方式ChaCha20-Poly1305 - ぼちぼち日記

RFC-7539 : ChaCha20 and Poly1305 for IETF Protocols
RFC-7905 : ChaCha20-Poly1305 Cipher Suites for Transport Layer Security (TLS)

えっ、別物???
私が調べないといけなかったのは「ChaCha20 and Poly1305」で、「ChaCh20-Poly1305」ではなかったということか。
最初に正しいリンク先を示しておきながら、どんどん違った方向にミスリードさせていくとは、なんたるトリックよ。。。

もちろん、ブログに書いているのは氷山の一角で、あれこれ調べたり悩んだりし続けていたのでした。


あまりにガックリしたので、今回はこれまで。

[clang]よくわからずchacha20する

「よくわからず」は、まだ甘い表現だ。
「まったくわからず」と言った方がよいのだが、取りあえずchacha20で何かしてみよう。

今回は、libsodumを使う。


[gist]libsodium1

よくわからないまま、chacha20という名前が入っていたAPIを使う。
。。。使おうとしたが、xchachaやchacha_ieftなどの種類があった。
結果も異なる。。。

ChaCha20-Poly1305のサンプルがあったので、少しだけ変更した。
MESSAGEに入れたデータは、encryptしてdecryptするとちゃんと復元される。

enc= 8eb13143515e31c709aabdb474a9f910a929b6389c4abaac90c62e5511bdb0ab1186

dec= 64(d) 69(i) 67(g) 69(i) 74(t) 61(a) 6c(l) 20( ) 74(t) 65(e) 6c(l) 65(e) 76(v) 69(i) 73(s) 69(i) 6f(o) 6e(n)

"digital television"が18文字で、encryptすると34byteだから、+16byte。
"test"は4文字で、encryptすると20byteなので、+16byte。
crypto_aead_chacha20poly1305_ABYTESが16byteだった。

 

いやあ、さっぱりわからんね。。。
ストリーム暗号だから、暗号にする単位ごとに16byteあればよいということだろうか。

2017/04/29

Poly1305

前回、Chacha20が使えるライブラリを調べた。
しかし、何か忘れている気がする・・・と思ったら、RFCではChacha20-Poly1305となっている。

Poly1305とはなんだろうか?


メッセージ認証符号とのこと。
Poly1305 - Wikipedia

メッセージ認証符号ってなんだ?と調べると、「メッセージを認証するための短い情報」ということだ。
メッセージ認証符号 - Wikipedia

MACは、FeliCaでも出てきたな。

INPUT1: 共通鍵
INPUT2: メッセージ
OUTPUT: MAC値

メッセージが変わると、MAC値も変わるので、書き換わってないことが確認できるということらしい。
デジタル署名と似たような感じがするが、あっちは秘密鍵で符号化して公開鍵で復号するタイプだった。
そして、秘密鍵で署名する場合は「秘密鍵を知っている=持ち主」という図式になっているので、署名を作った人はメッセージを符号化した人と同じと見なせるが、MACは共通鍵なのでそれができない(というのが、否認不可性をもたない、ということだろう)。

じゃあ、デジタル署名の方が優れているじゃないか、ということになってしまうが、MACの方が処理が軽いのかな?
デジタル署名ほどの機能は求めていないシーンもあるだろうから、そういう場合によいのかも。
この辺は、とても疎いので逃げておこう。。。

 

Poly1305の「1305」は、2130-5が素数だからら・・・って、いきなり素数のこと出てきてもわからん。

poly1305.dvi - poly1305-20050329.pdf

The Poly1305-AES formula is a straightforward polynomial evaluation modulo 2130-5;

うーん、moduloに関係していることは分かるのだが、まあ専門家じゃないので、ここも見なかったことにしておこう。

 

WikipediaのPoly1305には、オリジナルは「Poly1305-AES」と書かれている。
TLSでは、AESではなくChaCha20を使うと書かれているが、こっちは「ChaCha20-Poly1305」と順番が逆だ。
AESはブロック暗号で、ChaCha20はストリーム暗号だから、それを取り替えただけだろうと思うのだが、計算順序が違ったりするのだろうか?

 

まあいい。
既に自分で実装する気がないので、ライブラリさえあればよいのだ。
ともかく、ChaCha20だけではダメで、Poly1305も一緒にやらないとダメということはわかった。

前回調べた、ライブラリになっていないソースは、ChaCha20だけだ。。。というか、この人、ChaCha20自体の考案者やん!
Salsa20ときて、ChaCha20ときているから、ダンスの名称(だっけ)からとった名前のようだ。
そのうち、ジルバなんかも出てくるのだろうか。

ごにょごにょ調べていたが、ここを読んだ方がわかりやすいと思う。
新しいTLSの暗号方式ChaCha20-Poly1305 - ぼちぼち日記


libsodiumやwolfSSLはChaCha20-Poly1305と書いてあるから大丈夫だろう。
mbedTLSが対応するまで、それでしのぐしかないが、configureで移植できない環境に持っていくのは大変そうだ。

2017/04/28

ChaCha20が入っているライブラリ

お仕事で、ChaCha20というものが出てきた。
茶々?

リンクはここになっていた。
RFC 7539 - ChaCha20 and Poly1305 for IETF Protocols

stream cipherと書いてあるので、連続するデータをどんどん暗号化できるというものなのだろうか?

ストリーム暗号 - Wikipedia

ああ、AESなどはデータの塊を暗号化するからブロック暗号で、そういう制限がないのがストリーム暗号なのか。

 

残念ながら私は専門家じゃないので、難しいことは分からない。
知りたいのは、自分で実装できるようなものなのか、ライブラリを使った方がよさそうかということなのだが、こういう暗号関係は自分で実装しない方がよいだろう。
間違うと、ひどい目にあうし。

というわけで、ライブラリを探したい。


まず出てきたのが、libsodium。
ChaCha20-Poly1305 · libsodium

wolfSSLにもあった。
wolfSSL - Docs | wolfCrypt Manual - Chapter 18.7 (ChaCha20-Poly1305)

 

では、私がよく使っているmbedTLSにも・・・無かった。。。
Will ChaCha20 and Poly1305 be supported in mbedtls? · Issue #346 · ARMmbed/mbedtls

実装が進んでいる気配はあるが、もう1年前か。
Implement Chacha20 and Poly1305 by damaki · Pull Request #485 · ARMmbed/mbedtls

 

Cortex-Mくらいしか使わないから、小さいのがいいんだけどなぁ、と思ったら、こういうのがあった。
joostrijneveld/chacha-arm-cortex-m: Optimized assembly implementation of ChaCha8/12/20 permutation for ARM Cortex-M3 and Cortex-M4

Cortex-M3とかM4に最適化してあるらしい。
ほうほう、と中身を探したが、chacha20.hはあるが、本体がなさそうだ。
chacha20_perm_asm()をexternしているので、これが本体なのだとは思うが、Googleで検索しても出てこん。
本体は、libopencm3というARM cortex-M用のオープンソースライブラリなのかと思ったけど、うまく探せない。。。

 

ライブラリになっていると、各プラットフォームに移植するときに面倒だったりするから、数ファイルだけでできていて組み込めるのがよいのだけどなぁ。
Chachaだけだったらここにリファレンス実装があった。
The ChaCha family of stream ciphers

ref, regs, mergedって、なんの違いなんだ。。。
使い方がわからんので、同じソースを使っているこちらを読んでみるか。
circulosmeos/triops: triops: a multiplatform command-line encryption tool using CHACHA + KECCAK

2017/04/25

[et]EthereumはJavaScriptがわからないとつらかった

仕事ではほとんどBitcoinしか見ていないのだが、それじゃあんまりだということでEthereumも少し見てみることになった。

環境の作り方はいくつかあると思うが、私がやったのは、

という組み合わせだった。
これも自分で作ったわけではなく、作ってもらったのを使っただけだ。

geth(たぶん、go言語で書かれたethereumエンジンという意味だろう)を動かし、それにJavaScriptのAPIでアクセスするというやり方だ。

 

ethereumは、私から見ると、JavaScriptっぽい言語で動く仮想マシンだ。
EVM、EVMとよく出てくるけど、仮想マシンがEVMね。
JavaScriptのAPIでアクセスするから「JavaScriptっぽい言語」というわけではない。
Solidityという言語でプログラムを書いて、それをブロックチェーンに載せると、燃料(gas)を与えればプログラムが動く、というイメージだ。

JavaScriptからはAPIを経由してSolidityで書いた関数を呼び出す。
関数ではブロックチェーンに書込んだり読込んだりができる。
なんとなく、DBアクセスしている気分だ。

ただ、DBじゃないので、書込んだ値は「自分のノードでそう思っている」というだけで、マイニングされてブロックに組み込まれないと値が確定しない(のだと思う)。
この辺りは、しくみがよくわからんかった。
ライトキャッシュみたいに、自分が書込んだ値を読込めば本体にアクセスせずに読み込めるけど、書込んでない人は本体を読みに行くので、キャッシュが吐き出されるまでは値が変わらない、みたいなことじゃないかなぁ。

 

ドキュメントの更新はそんなに頻繁ではないようで、APIを使ってみたらもう搭載されていなかった、ということがときどきあった。

私が触っていた頃は、ちょうどtestenetのRopstenが攻撃を受けたところで、Parityという別実装のエンジンだと対処法があったのだけど、Gethではどうしようもなさそうだったので、プライベートチェーンを作って試して終わった。


とにかくつらかったのが、Solidityとかじゃなくて、JavaScriptそのもの。
使い慣れていない上に、スクリプト言語自体に不慣れなので、毎日荒れていた(私が)。

また、便利なはずのmeteorも、私にとっては「JavaScriptがよくわからないのに、さらによくわからないものが載っている」ということになり、いろいろつらかった。

つまり、そういう言語に使い慣れている人であれば、敷居はそんなに高くないのかもしれない。
JavaScriptが書ける人であれば、ブロックチェーンのところはSolidityを使える少数の人にやってもらって、周りのアクセスするところは普通のJavaScriptが書ける人で対応できる。

ネットニュースでブロックチェーンを使った実験とかあるけど、多少複雑なことをブロックチェーンだけで完結させたいと思ったら、何かしらブロックチェーンでプログラムが動いてくれる方が便利で、そうなるとEthereumか、という選択になるのだろう。
技術者を集めやすいし、JavaScriptで書ける人はブラウザを使ったサービスなんかをよく作りそうな気がするので、画面の見栄えもよさそうだ。

 

Ethereumはホワイトペーパーとイエローペーパーというものがある。
ホワイトペーパーは日本語訳もあるので、ちょっと読みづらいけど中身としては興味深かった。
Bitcoinを分析しているところなんかも面白い。
[Japanese] White Paper · ethereum/wiki Wiki

イエローペーパーは、いやあ、さっぱりわからなかった。。
http://yellowpaper.io/

このくらいできないと、ブロックチェーン技術者になれないのだろうか・・・。

Windows10 Creators Updateの様子

うちで動いているPCのうち、1台だけCreators Updateになった。

今のところ、特に困ったことはない。
IMEがATOKを使っていたのに切り替えられていたり、高速スタートアップが有効に戻されていたり、システムフォントが元に戻っていたりと、いくつか元に戻されていた。

得に便利になったというものはないのだが、Bash on Ubuntu on Windowsの動作がいろいろLinuxのUbuntuに近くなったので、それが一番ありがたい。
今のところ、valgrindも動いている。

 

さっき気付いたが「ディスプレイ」の設定に「夜間モード」というものがあった。
ブルーライト軽減というやつかな。
眠たくなりそうな色なので、ちょうどよいかも。

では、おやすみなさい。。。

2017/04/17

Windows10の更新(2017年春)

2017/04/17 23時半

風呂から上がってくると、画面が薄暗くなっていて、中央にこんなダイアログが出ていた。。。

image

「設定の確認」をクリックすると、プライバシー設定の項目が5つくらい表示されて、ON/OFFできるようになっていた。
とりあえず、全部OFFにして承諾すると、バックグラウンドで何かするようなことをごにょごにょ言って、また元の画面に戻った。

 

Windows Updateの画面を見ても、タスクトレイを見ても、何かしているようには見えない。
タスクマネージャーを起動しても、何かダウンロードしている感じでもない。

うーん、なんだったのだろうか?


2017/04/18 8時半

Windows Update画面の更新ボタンを押すと、ダウンロードが始まったようだ。。。

image

日曜日にバックアップを取っているので、このまま進めてみよう。
今回のPCは、Windows標準のバックアップを使っているので、何が保存されているのかわかりづらいのだがね。

 

ダウンロードが終わると、自動的にインストール準備が始まった。
なお、これはWindows10 64bit Homeなので、Updateについては制御できないと思う。

image

 

放置して作業していると、ばーん、とダイアログが出てきた。

image

覚悟は・・・できている・・・。


30分くらい放置していると、いつの間にかアップデートが終わっていた。

 

違いはよくわからんが、アクティブ時間が18時間まで設定できるようになった。

image

 

フォントが、また細いのに戻されたから、元に戻したり。
Defenderを使っているのだが、サンプルの送信を無効にしておくと警告マークが出てくるので気持ち悪い。。ここは我慢だ。

メニューに「更新プログラムとプライバシーの設定」というのが追加されているのだけど、選択してもブラウザが開いてページに飛ぶだけだった。

 

あとは、Bash on Ubuntu on Windowsのアップデートくらいか。
valgrindやgolangがちゃんと動くようになっているとよいのだが。

Bash on Ubuntu on Windowsの、Creators Updateでの強化点&新機能【Insiders Preview版】 - Build Insider
BoWの再インストールがいるらしいが、ホームディレクトリにあるデータも消えるとは。。。
ESP8266のRTOSビルド環境や、RaspberryPiのkernelビルド環境を作っていたのだが、もう一度構築し治した方がよいだろうな。

uninstallコマンドを打つと、消えるけどよい?と確認してくれるのは親切だ。
アンインストールは、案外早く終わる。

インストールも同じようなものかと思ったが、ダウンロード後にプロンプトが出てきた。

image

えーっと、これで"y"入力するとja-JPに変更されるのか?
それとも続行して、ja-JPに変更するか聞かれるのだろうか。
あるいは、ja-JPに変更しつつ続行か。。。
じゃあ、"n"にしたらどうなるのか。。。

最初からやり直せばいいや、と"n"にすると、アカウント作成に進んだ。
アカウント作成すると、終わりだ。
Bash on Windowsのプロンプトを起動して、確認。

$ echo $LANG
en_US.UTF-8

lsすると、Esp8266などのフォルダはそのまま残っていた。
.viminfoは残っているので、最初からあったと思われる.bashrcが消えただけか。

valgrindをインストールしようとしたが怒られるので、apt update/upgradeしておく。
makeもgccもg++もないので、あれこれインストール。。。
いつも何も考えずに足りないものをインストールしているけど、build-essentialだけやっておけばよいのかも。

googletestのビルドに失敗するので、libgtest-devとgoogle-mockもinstall。
・・・だめだ。
c++11 - Converting std::__cxx11::string to std::string - Stack Overflow
ああ、gccのバージョンが変わったということか?
Makefileに-D_GLIBCXX_USE_CXX11_ABI=0を追加して、ビルドできた。

valgrindは、動いているような気がする。
これは様子見だ。
そういえば、以前はvalgrindも自分で何とかしないと行けなかったような気がするが、apt installしたもので動きそうな気配だ。


2017/04/19

前回は、_GLIBCXX_USE_CXX11_ABI=0を追加してビルドできたのだが、今日はダメだ。。。
Ubuntu16.04でもダメだった。
=1にするとビルドできたので、make cleanが中途半端な状態になっていたのかもしれない。

 

valgrindは動いているようなのだけど、私が使っているライブラリではcalloc()を内部で使っているらしく、calloc()があるとvalgrindが誤検知するというか、解放タイミングが違うために解放漏れとして検知してしまうようなのだ。

-fsanitize=leakでやるのがいいんじゃないの?というStackoverflowがあったので試したのだが、BoWではビルドできるものの実行すると固まってしまった(valgrindは使ってない)。
Ctrl+Cでもタスクマネージャーでもすぐには効いてくれず、かなり時間がかかってから停止した。
むう。。。

組み込み環境だとメモリリークのチェックは大変だから、できるだけこういう環境でチェックしておきたいのだがなあ。

プロトコル実装するライブラリを探したが、用途に合うものが無かった

2点間で通信しようとすると、まず通信仕様を決めなくてはならない。
プロトコルというやつだ。

物理的な通信仕様が決まったら、次は論理的な通信仕様を決めて、どこかの段階でデータの仕様も決めることになるだろう。
あるいは、データ仕様はRFCだったり既存の機種だったりで決められているものかもしれない。

 

さあ、ではこれを実装しよう、となったとき、内部で持っているデータ構造をプロトコルに合わせなくてはならない。
これが面倒で、比較的機械的な作業なのだけど、やらないといかん。

ただ、そういうものであれば、誰かユーティリティを作っていないだろうか?


まず思いついたのは、protobufだ。
名前はあちこちで見かけるので、使えそうな気がする。
しかしWikipediaを見ると「XMLとの比較で~」などと書かれているので、protobufフォーマットのプロトコルで通信したり、ファイルに保存したりするのであればよさそうだけど、決められたデータ形式に合わせた入出力にする、というものではないのだと思う。
Protocol Buffers - Wikipedia

 

他にもシリアライザーという種類のライブラリはあったのだけど、やはり独自フォーマットになるようだ。
まあ、よく考えれば、「プロトコル」と一言で書いても、テキストやバイナリがあるし、バイナリだってType-Length-Valueもあれば、Length-Type-Valueもあるし、データに構造が載っておらず順番だけで決めてしまうものもあろう。

そういうものに対応したライブラリを作ろうとしたら、作りが複雑になって、使うのも複雑になって、結局誰にも使われない、ということになってしまいそうだ。
だから、そういうライブラリはないんじゃなかろうか、という結論に至った。


では、部品レベルでもよいから作っておきたい場合、どういうものがよいだろうか。

私の場合、だいたいバイナリで通信するから、まずはバイナリが前提となる。
TLVなり、LTVなりが固定されている方がありがたい。ソースファイルだけ取り替えれば構造が変わるのがよいだろう。

あとは、エンディアンとアラインメントか。
私が使う環境はリトルエンディアンが多いのだが、プロトコルはビッグエンディアンだったり、ものによってはビッグとリトルが入り交じっていることもある。
そして、プロトコルの方は無駄がないようにデータを詰めるけど、プログラム上ではメモリ境界に沿わないと使えない。

アラインメントは、gccだと#pragma packで詰めたりもするのだけど、いつも罪悪感というか、そういうものを感じてしまう。。。
気にせずアクセスできるCPUならよいけど、そうじゃなかったら裏でコードを書いて対応していたと思うので、全体的にサイズが大きくなってしまうこともありそうだ。

 

そんなことを考えていくと・・・部品化せずに全部実装することになるんじゃないのか?
プロトコルの内容によって、機械的に解釈する方が効率的だったり、個別に解釈する方が効率的だったりするのを考慮してしまうと、部品化できなくなってしまうのだ。

そうではなくて、メンテナンス性の良さとか、今後の使い回しとか、そういうのも含めて考えないと。。。
とは思うのだが、また今回も個別に実装するような気がしている。

2017/04/15

[c#]フォルダ選択ダイアログを、普通のファイル選択ダイアログ風にしたい

久々にC#だ。

Windows10を使っているのだが、フォルダ選択ダイアログとしてToolboxに入っているFolderBrowserDialogを使ったのだが、初期フォルダをツリーの下の方にあるようなフォルダにしておくと、表示させたときにそこまでスクロールしてくれないことがわかった。

何か設定すればよいのかと思ったが、そうではなくて、このしくみ自体が.NETの部品じゃなくていじるのが面倒な様子がStackoverflowに書かれていた。
うん、そこまでしてやりたくはない。

 

そういえば、私が使っているEmEditorというテキストエディタでは、フォルダ選択が普通のファイル選択風になっているのを思い出した。
それなら、スクロールなど考慮せずに済むはずだ。


ようやく出てきたのが、こちら。

ファイルは選ぶ事あるけど、フォルダを選ぶことは少ない。 | C#等と戯れる日々

NuGetでWindows7APICodePack-Shellというものをインストールし、Microsoft.WindowsAPICodePack.Dialogs.CommonOpenFileDialogをnewして、IsFolderPickerをtrueにすることで、期待するダイアログが表示できた。

image

ボタンが「フォルダーの選択」になっていて、ファイルが表示されないようになっていた。

 

ただ、初期フォルダを設定しなかった場合、どこが開かれるのかがよくわからん。
なんとなく、前回最後に開いたフォルダのような感じはする。
System.Windows.FormsのInitialDirectoryがそうだから、同じような動作になるのかな?
このCode PackでもInitialDirectoryプロパティは使えるようになっていたから、指定した方が無難かもしれん。

2017/04/13

[bc]アドレスを作るまでに得られた結果の使い方

前回、秘密鍵、公開鍵、公開鍵ハッシュ、Bitcoinアドレス(P2PKH)の求め方を説明した。
今回は、それぞれの使いどころを説明しよう。

 

まず覚えておいてほしいのは、こういう前提だ。

  • 公開鍵ハッシュからBitcoinアドレスを求めることができる
  • Bitcoinアドレスから公開鍵ハッシュを求めることができる
  • 公開鍵から公開鍵ハッシュを求めることができる
  • 公開鍵ハッシュから公開鍵を求めるのはほぼ不可能
  • 秘密鍵から公開鍵を求めることができる
  • 公開鍵から秘密鍵を求めるのはほぼ不可能

秘密鍵→公開鍵→公開鍵ハッシュ、の部分は一方向ハッシュという計算を使っている。
A→Bは計算できるし、毎回同じ計算結果が得られるが、B→Aは計算できない、というやつだ。

「ほぼ不可能」としたのは、力任せに一方向ハッシュの計算をし続ければ、いつかは期待する値を見つけることができるからだ。
たとえば秘密鍵は32byte=256bitなので、2256回試せば、そのどこかで出てくるはずだ。

image

1秒間に1億回計算できたら、何秒かかるだろうか?

image

うーん、生きてる間には無理そうだ。。。
まあ、0から計算を始める必要もないと思うが、割に合う作業ではないように思う。
ノイマン型のコンピュータだときついが、並列世界を作り出して正しい答を持つ世界だけを選択できるようなことができれば。。。


こういう難しい話は賢い人やずる賢い人が考えてくれていると思うので、今回は単純に「逆方向の計算はできない」ということにしておく。

 

まず、Bitcoinアドレス。
これは人間が見るためのものだ。
Bitcoinのブロックチェーンとしては使っていない。

 

次は公開鍵ハッシュ。
これは、「このアドレスに送金します」という出力で使われる。

image

左が送金元、右が送金先だ。
青い文字がBitcoinアドレスだが、右側の「scriptPubKey」のところに、OP_DUP、OP_HASH160に続いて16進数がずらずら並んでいるのが見えると思う。
これが、公開鍵ハッシュだ。

試しに「mwtvLXha2y5wiMt3XGFGx7EpTNG9pMdQLw」をBase58デコーダで戻してみよう。

image

「なってないやん!」と思うかもしれないが、落ち着いておくれ。
Bitcoinアドレスに変換する際、公開鍵ハッシュにプレフィクスとサフィックス(CRC)をつけているのだ。
頭1byteとお尻4byteを切り取って、

6FB3A987A2561CFFDFCFB0FE90AFD309576D7F88295FBF2A80

一致したでしょう?

 

秘密鍵はデジタル署名をするために、公開鍵はデジタル署名したのが誰かを示すために使われる。
Bitcoinの支払いは送金元と送金先を示すので、送金元の人が許可していないと送金できないようにしたい。
だから「私が送金を許可しました」という意味で、送金の記録(トランザクション)に送金元の人がデジタル署名を行うのだ。

デジタル署名の計算は、秘密鍵を使う。
秘密鍵を持つ人じゃないとBitcoinの支払いを行うことができないことになる。
ただ「これは私の秘密鍵で計算したデジタル署名ですよ~」と宣言しても信用できないので、チェックする方法が必要になる。
そのチェックに秘密鍵が必要になってしまうと意味が無い(秘密鍵を持っている人しか署名できないというルールが崩れる)。
そこで公開鍵があれば、トランザクションとデジタル署名のセットが正しいかどうかのチェックができるという計算式がある。

 

なので、秘密鍵だけは絶対にバラしてはいけないのだ。
そのBitcoinアドレスに入っているお金は、秘密鍵を知っている人なら誰でも送金できるようになるからだ。
カードの暗証番号みたいなものだと思えば良いか。

もう1つバラしてはいかんのが、ウォレットアプリのバックアップ。
これは秘密鍵よりもまずい。
Bitcoinアドレスの秘密鍵だけなら、そのアドレスに入っているお金だけが対象なのだが、ウォレットアプリのバックアップが漏れてしまうとウォレットに入っているすべてが対象になってしまうのだ。。。

だから、こういうのはクラウドに置いたりせず、できればPC内にも置かず、ネットや人目から切り離されたところに置いておきたいところ。
私は実験用にしかBitcoinを持っていないのであきらめられるくらいの額なのだけど、心配ならハードウェアウォレットを検討すべきだろう。

2017/04/11

[c/c++]gccではglobalでstructのinitializerにconst intは代入できない

タイトルを見てもなんだかわからないと思うので、ソースを載せる。

#include <stdio.h>

static const int A = 1;
static const char B[] = "abc";

static const struct {
    int a;
    const char *b;
} VAL[] = {
    { A, B }
};

int main(int argc, char *argv[])
{
    printf("a=%d, b=%s\n", VAL[0].a, VAL[0].b);

    return 0;
}

これをgccでコンパイルすると、エラーになる。

$ gcc -Wall -o tst test.c
test.c:10:7: error: initializer element is not constant
     { A, B }
       ^
test.c:10:7: note: (near initialization for 'VAL[0].a')

 

確かC++だと、const値は定数扱いだったような気がするので、g++にしてみる。

$ g++ -Wall -o tst test.c
$

うん、OKだ。

 

ただ、gccでもこれはOKだ。

#include <stdio.h>

static const int A = 1;
static const char B[] = "abc";

int main(int argc, char *argv[])
{
    const struct {
        int a;
        const char *b;
    } VAL[] = {
        { A, B }
    };

    printf("a=%d, b=%s\n", VAL[0].a, VAL[0].b);

    return 0;
}

staticを外して、関数の中に入れただけだ。
もしstaticを外さなかったら、やはりエラーになる。

やっぱりCだと、constはダメなのか。

 

これは、通るのだ。

#include <stdio.h>

//static const int A = 1;
#define A (1)
static const char B[] = "abc";

static const struct {
    int a;
    const char *b;
} VAL[] = {
    { A, B }
};

int main(int argc, char *argv[])
{
    printf("a=%d, b=%s\n", VAL[0].a, VAL[0].b);

    return 0;
}

整数の方だけをマクロにしたのだ。
なぜポインタの方はエラーにならないのだろうか?

 

#include <stdio.h>

//static const int A = 1;
#define A (1)
static const char B[] = "abc";
static const char *C = B;

static const struct {
    int a;
    const char *b;
} VAL[] = {
    { A, C }
};

int main(int argc, char *argv[])
{
    printf("a=%d, b=%s\n", VAL[0].a, VAL[0].b);

    return 0;
}

Bは、ポインタと言うよりは、そのアドレスからデータが始まっているからよいのか?
だったら、intだって似たようなものだと思うのだ。

納得がいく理由が導き出せないが、g++だと通るところからすると、値を解決するタイミングが違うとか、そういうことだろうか。。。

[mqtt]v3.1とv3.1.1

ESP8266でMQTTを動かすため、他のMQTT clientを動かしてwiresharkログを取っていたのだが、CONNECTのプロトコルが最初違っていた。
設定が「v3.1.1準拠」になっていたので、それをやめると期待する値になった。

 

私が期待していたのは、v3.1で見ていた値だ。
http://public.dhe.ibm.com/software/dw/webservices/ws-mqtt/mqtt-v3r1.html#connect

Protocol Nameが"MQIsdp"で、Protocol Version Numberが3だ。

 

しかし、v3.1.1準拠だと、こうだ。
http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Toc398718030

Protocol Nameは"MQTT"で、Protocol Version Numberは"Protocol Level"という名称になり、4となっている。

 

そういえば、Ubuntu14で動かしたとき、v3.1設定にしないと動かなかったのだが、CONNECTが違っていたのか。
理由が分かってすっきりした。

しかし、v3.1の"MQIsdp"はどういう意味だったのだろうね?
sdpはService Discovery Protocolのような気がするが、MQIはわからん。

http://mqtt.org/faq
違った。
"MQ Integrator SCADA Device Protocol"の略だと。
知ったふりをしなくて良かった。。。

2017/04/09

[esp8266][rtos]MQTT publisher

MQTT v3.1の仕様を眺めたり、他のMQTT clientを動かしてwiresharkでログを取ったりしながら、ESP8266 RTOSでMQTT publishだけするアプリを作った。

https://github.com/hirokuma/Esp8266SimpleMqtt

 

仕様は、この辺を読んでいる。
https://www.ibm.com/developerworks/jp/websphere/library/wmq/mqtt31_spec/

ドキュメントとしては、あんまりわかりやすくないと思う。。。
が、日本語で書いてあるので、助かる。

 

これはプロトコルバージョン3なのだけど、wiresharkで見ていたクライアントは4のようで、校正が違っていた。
まあ、気にすまい。

2017/04/07

[bc][slack]botを作ってみる

朝起きると、hiro99maは一匹の巨大な毒虫に・・・なってはいなかったが、なぜかslackのbotを作ってみたくなっていた。

仕事の連絡手段としてslackを使っているのだが、左上に「Apps」という項目があることに気付いた。
どうやら、プラグインのようなものと連携して、挙動を追加できるらしい。

ほほう!

そういえば、botというやつで何かできた記憶がある、と思い出し「slack bot」で検索しようとしたら、候補に「python」が出てきた。
pythonなら多少は使えるので、せっかくだから何か作ってみよう。

何がよいか・・・。
ああ、最近、仕事に関係しないbitcoinネタを書いているので、それにしてみよう。


PythonでSlackbotを作る(1) – ビットログ

一番上に出てきたので、これを参照した。
さすが一番上だけあって、読んでできた。

第1回で、botを使えるようにするまでできた。
API Tokenの取得などだ。
なお、私はpython2を使った。
素直に書いてあるbotの実装も試しておこう。

第2回を読めば、だいたいやりたいことがわかる。
私の場合は、こちらからBitcoinのTXIDを伝えて、blockexplorer.com(リンク先はtestnet用)みたいなサイトから情報を取ってきて、テキストで吐き出そうと思ったので、このくらいでよいのだ。


作ったのは、こちら。
https://github.com/hirokuma/slackbot_bitcoin_txid

API Tokenは自分で埋めておくれ。
あと、testnet用にしているので、必要に応じて修正しよう。
難しいことはやってないので、pythonがわかる人なら変更もすぐできるんじゃなかろうか。

 

困ったのは、むしろtestnetの挙動だった。
なんか、ちょうど作っている最中、攻撃が行われていたようなのだ。
やーねー、もう。

2017/04/06

[bc]アドレスを作るまでの計算

前回は、Bitcoinのアドレス、と一言にいってもいろいろ種類がありますよ、ということを書いた。
そして、先頭の文字を見ると、だいたいどういう種類かや、お金に関わる方のBitcoinブロックチェーン(mainnet)なのか、お金が関わらない方のBitcoinブロックチェーン(testnet)なのかわかる、というところまで説明した。

 

わざわざ「Bitcoinのブロックチェーン」と書いているように、ブロックチェーンにもいろいろ種類がある。
というよりも、それぞれ別のブロックチェーンのネットワークを作っている。
例えば、mainnetとtestnetは、ブロックチェーンの構造としては同じだが、TCP/IPのポート番号が異なる。
Ethereumという、こちらも有名なブロックチェーンを使った仮想通貨があるが、これはこれで別のブロックチェーンだ。

「ブロックチェーン」という1つのものをみんなが使っているわけではなく、めいめいが自分が好きなようにブロックチェーンを作っているのである。


では、話を戻そう。

Bitcoinアドレスは、似たような文字列でできているように見えるが、そもそもどうやってアドレスを作っているのか、という話をしたい。

http://chimera.labs.oreilly.com/books/1234000001802/ch04.html
ここに全部書いてあるので、読むとわかる。

・・・ではあんまりなので、大雑把に説明しよう。
なお、私はtestnetでしかやってないので、説明もtestnetだ。

 

Pythonでの実装例がExample4-4に載っているので、それを見ていこう。
http://chimera.labs.oreilly.com/books/1234000001802/ch04.html#_implementing_keys_and_addresses_in_python

まず、32byteの乱数を用意する。
16進数32個分だ。
http://chimera.labs.oreilly.com/books/1234000001802/ch04.html#ec_math
Example4-6の、

byte_array = os.urandom(32)

がそれにあたる。
この値を「秘密鍵(private key)」と呼ぶ。

 

秘密鍵は単なる大きい値というだけで、この次に楕円曲線暗号というものが出てくる。
楕円、ではなく、楕円曲線、だ。
金魚鉢のような、オバQのO次郎を横向きにしたような、そんな形だ(Figure 4-2)。
http://chimera.labs.oreilly.com/books/1234000001802/ch04.html#pubkey

楕円曲線にもいろいろ種類があるのだが、とにかくBitcoinで使用する楕円曲線上の点Gに、楕円曲線座標系での乗算を行うことで「公開鍵(public key)」が求められる(Figure 4-4)。
もちろん、点Gに掛けるのは秘密鍵だ。
http://chimera.labs.oreilly.com/books/1234000001802/ch04.html#ecc_illustrated
この図では、G→-2G→+2G→-4G→+4G→-8G→+8G、という演算が行われている。
もし秘密鍵が「8」だったら、この8Gが公開鍵の座標になる。
なんで接線方向に移動したりX軸に直角に移動したりするの?と問われても、私にはわからん。。。

公開鍵は座標なので、(x, y)の形式になる。
ノーマル形式の公開鍵は、このxとyを連結した値になる。
秘密鍵が32byteだからか、xも32byte、yも32byteにするようで、連結すると64byteになる。
それだけだとなんだかわからないので、「0x04」を頭につけて「ノーマル形式の公開鍵」という表現の仕方をすることになっている。
だから、65byteだ。

ただ、楕円曲線を見るとわかるように、X軸に対して線対称だ。
では、X座標とそれに対応するY座標の正負情報だけで公開鍵を表して短くしよう、という説明がFigure 4-7だ。
http://chimera.labs.oreilly.com/books/1234000001802/ch04.html#pubkey_compression
x座標からy座標を求める演算はy2 mod p = (x3 + 7) mod pになるため、y2を解くと±の結果になるから、どっちなのよ、ということを示すために、ノーマル形式の「0x04」に対して「0x02」 or 「0x03」という値を頭につけて、33byte形式にしたのが「圧縮された公開鍵」と呼ばれるようになっている。
今では、「0x04」の公開鍵はデータが大きくなるのでほぼ使われていないと思う。

 

公開鍵に、SHA256というハッシュ計算をすると、32byteになる。
それにRIPEMD160というハッシュ計算をすると、20byteになる。
理由は知らんが、Bitcoinではハッシュ計算を2回連続して行うことが多い。
ともかく、公開鍵をSHA256→RIPEMD160、という計算で出てきた20byteの値を公開鍵ハッシュ(Public Key Hash)と呼ぶ。
通称、PubKeyHash。
略称、PKH。

 

PKHに、前回出てきたmainnet/testnetのプレフィクスをつけ、CRCをサフィックスにつけ、それをBase58Checkというエンコード方式で演算すると、Bitcoinアドレスが出てくる。
Base64という、メール添付するときのエンコード形式から、目で見て見分けにくい文字を減らして58文字にしたのがBase58らしい。
Base58はハッシュではなくエンコード/デコードなので、今回で言えばBitcoinアドレスから公開鍵ハッシュまで戻すことはできる。
http://lenschulwitz.com/base58

 

公開鍵を公開鍵ハッシュにした値からアドレスを求めるので、このアドレス方式はPublic to Public Key Hash→P2PKHと呼ばれる(のだったと思う)。

このようにして、秘密鍵、公開鍵、公開鍵ハッシュ、Bitcoinアドレス(P2PKH)が得られる。
お疲れ様でした。

[c/c++][make]dependの解決

以前、こんな記事を書いた。

hiro99ma blog: [c/c++]Makefileのdependは名前一致なのか

ソースファイルの依存関係をMakefileに反映させるためにgcc -MMを使ったが、./のように関連とディレクトリを含むような書き方にするとダメだった、という話だ。

 

そして、同じ問題にまた遭遇してしまったのだが、対処するのにかなり時間がかかってしまった。
悔しいので、それっぽいものをgithubに上げることにする。

https://github.com/hirokuma/makefile_hello

 

 

しかし、dependだけではなく、makefileは書くのが難しいな。
今回だって、難しいことがしたかったわけではなく、ディレクトリをsrc, include, objectsに分けただけだったのだ。
Eclipseでプロジェクトを作るとうまいことやってくれるが、あれは偉大なのだな。
まあ、あれはあれで、うまくいかなかったら解決に時間がかかるのだが。。。

2017/04/05

RFC 6979を使った結果がライブラリによって違った→私の間違いだった

2017/04/05更新:mbedTLSに問題がなかったので更新

突然のRFCだ。
ESP8266のこともやりたいのだけど、仕事の調査もあるのでね。。。
せっかくなので、書いても問題なさそうなところは使おう。


RFC 6979という、楕円曲線を使った署名についてのRFCがある。

RFC 6979 - Deterministic Usage of the Digital Signature Algorithm (DSA) and Elliptic Curve Digital Signature Algorithm (ECDSA)

私の把握では、

  • 楕円曲線暗号の署名の乱数って、面倒よね
  • そもそも、乱数が乱数かどうかなんてあやしいし
  • 別の署名で同じ乱数が使われたりすると、目も当てられないわ
  • じゃあ、いっそのこと、データによって乱数の値が変わるような計算式を作ってしまわない?
  • さんせいさんせい!!

というところだ。
RFC 6979に基づいた署名計算を行うと、乱数要素がなくなるので、毎回同じ署名結果が得られる。


それはよいのだが、この決定性署名(Deterministic Signature)を持つ暗号化ライブラリを使ったのだが、結果は乱数を使っていないので同じ結果になるものの、ライブラリによって得られる署名が異なることがあった。

やむなくRFC 6979を見てみたが、うん、全然わからん。。。
ただ、3.2章が「Generation of k」で、3.3章が「Alternate Description of the Generation of k」となっている。

Alternate Description ?
別の説明、ってなんだ?
3.2章で使った用語とは別の言葉(X9.62の用語か)で説明しているだけだとは思う。

たぶん、kは、ここの手順3の整数kだと思う。
「セキュリティ」のところにもそう書かれていた。
https://ja.wikipedia.org/wiki/%E6%A5%95%E5%86%86%E6%9B%B2%E7%B7%9ADSA#.E7.BD.B2.E5.90.8D.E7.94.9F.E6.88.90

 

一致しているライブラリは、先にnonceを求めてから署名を行っている。
nonceは、たぶんkのことだろう。乱数っぽいし。
困ったことに、一致していないライブラリは違う計算方法で、直接比較ができない。

mbedTLSなのだが、うーん、どうしたものか。。。
https://github.com/ARMmbed/mbedtls/blob/development/library/ecdsa.c#L72
nonceを作らず、f_rngでうまいことやるようなのだが、乱数っぽい値はtに入るようだし、kはkeypairとかから設定されているし、なんだかよくわからんのだ。


2017/04/05

この問題はけっこう大きいので、引き続き調査を行う。
まあ、違う実装を使えばよいと言えばよいのだけど、それでは負けた気がしてしまう。。。
mbedTLSは負けないのだ!

間違われないように書いておくが、署名が間違っているわけではないのだ。
verifyは成功する。
単に、署名が一致しないだけなのだが、それがあり得るのかどうかだけが気になっているのだ。

 

mbedTLSの実装でDeterministicっぽいことをしているのはここだと思う。
https://github.com/ARMmbed/mbedtls/blob/development/library/ecdsa.c#L178-L182

よくわかってないが、HMAC_DRBGという言葉が出てくるので、RFC6979の3.3章のような実装なのだろうか。
この辺で設定したrng_ctxを使ってECDSAの署名に使う乱数を作っているのだろうと思う。
呼び出すのは、mbedtls_hmac_drbg_random()
これを使っているのが、L97のgen_keyapir()と、L121のfill_random()
RFCと実装が一致しているのかを確認するのは、私にはちょっと難しい。。

でも、mbedTLSもちゃんとtestsuitesでRFC 6979に載っているデータのパターンはOKなのだよ。。。
だから、間違えているとは考えにくい。
例えば、RFCのA.2.3は、この辺がテストデータになっている。

 

全部正しいのに結果が違うということがあり得るとしたら、エンディアンじゃなかろうか?
RFC 6979にはbig-endianと書かれているけど、与えているハッシュ値がlittle-endianになっているからとか。
しかし、いろいろひっくり返したものの、署名は一致しなかった。

なんでだ??
そんなことあり得るのか???

 

わからなくなって、変更したところを元に戻し、自分のmbedTLSを使った処理を見直してみた。
あれ・・・署名計算だけしか見てなかったから気付かなかったけど、そもそもの署名計算に与えている値を間違えている・・・?
pythonで作ったのと同じパラメータになるように修正して・・・

あーーー、一致したーーーー
疑ってごめん、mbedTLS。
やっぱり私が間違っていたよ。。。

2017/04/04

[esp8266][rtos]ほどよいMQTT client

せっかくESP8266でSmartConfigすれば外部からWiFi APの設定ができることが分かったので、なにか動くアプリを作りたい。
ただ、今はセンサも何もつないでいないので、定期的にどこかに送信する、程度のことができれば良い。

であれば、MQTT brokerに向かってpublishするだけ、というのが簡単そうな気がする。
ほどよいMQTT client APIがなにかあるだろう。


Pythonではpahoを使っていたが、そのC版をESP8266のRTOS版に移植したものがあった。
baoshi/ESP-RTOS-Paho: Port of Eclipse Paho C client on ESP8266

よさそうなのだけど、ちょっと大きいなぁ。
メモリに制限があるから大きなpublishをすることもないし、QoSも0しか使わないだろうから(publishにQoSあったっけ?)、v3.1くらいの内容であれば自分で実装してもよいかもしれない。

でも、既にできているプロジェクトを取り込むのも勉強になるし。
うーーーーむ。。。

 

そうか、両方やればいいんだ。
自分で作ると柔軟性も拡張性もないがサイズが小さなライブラリができるだろうから、それはそれで使い道があるかもしれない。
その後でPahoを移植したライブラリを組み込めば、あれこれできるようになるので、それもよいことだ。

むかし仕事で作った実装を見るのはさすがに反則だから、ちまちまやっていこう。

2017/04/03

[bc]segwitのこと

Bitcoinで、segwitというのがある。
segwitは"Segregated Witness"の略なのだが、まあ語源はどうでもよい。
Bitcoinがハードフォークするとかしないとか話が出ているが、そういうのもどうでもよい。
私はsegwitの話をしたいだけだ。


取引の内容を表す「トランザクション」は単なるバイナリデータなのだが、いくつかの部分に分かれている。
部分の正式名称は、こんな感じだったと思う。

  • version
  • [marker]
  • [flag]
  • txin
  • txout
  • [witness]
  • locktime

カギ括弧を付けているものが、segwitで追加された部分だ。

 

そして、話が面倒なことに、作り方によってトランザクションに入れ込むデータが変わってくる。
ここではscriptPubKeyと書いたが、これはたぶんP2PKH向けの名前で、locking scriptという呼び方の方が正しいかも。
txout(txは"transaction"の略で、inが入力、outが出力)に書くスクリプトの部分だ。

  • segwitではないscriptPubKey
  • segwitで、データがちょっと長めのscriptPubKey
  • segwitで、データがちょっと短めのscriptPubKey

「ちょっと長め」ってなんだよ、となるが、これはsegwit対応していないウォレットアプリからでも送金先として指定できるタイプである。
見た目はP2SHなのだけど、中身はsegwit向けというやつだ。
通常がP2PKHとしたら、そのちょっと長めバージョンはP2WPKH nested in BIP16と呼ばれる。
データだけじゃなくて、名前も長い。。。

 

「ちょっと短め」ってなんだよ、ともなるが、これはsegwitに対応したウォレットじゃないと送金先に指定できない。
こっちは単にP2WPKHと呼ばれている。
nested inしている方のscriptPubKey(0xA914{20-byte-script-hash}87=23byte)に比べると、1byte小さい(0x0014{20-byte-key-hash}=22byte)ことがわかる。

これだけだと大差ないのだが、このトランザクションをInputとするトランザクションを作ると差が出やすい。
witnessに「<signature> <pubkey>」を置くのはどちらも同じなのだが、nested inの方はscriptSigに23byteのデータを置くことになっている。

 

ただ、ふつうのP2PKHと比べると、ちょっと短めの方でsegwitのトランザクションを作ったとしても、そっちの方がわずかにサイズが大きい。
nested inになれば、なおさらだ。
大ざっぱにsegwitのP2WPKHトランザクションを作ろうとすると、

  • ふつうのP2PKHみたいな構造を作る
  • [marker]と[flag]を追加する(+2byte)
  • scriptSigに置いていた<signature>と<pubkey>をsegwitに移動
  • nested in BIP16なら、scriptSigに代わりのデータを置く(+23byte ?)

というところか。
lengthの付け方がどうだったか忘れたので、1,2byteくらいは違うかも。
あと、署名の計算方法も変わるのだけど、サイズはほぼ同じだからいいや(サイズの範囲は非segwitと同じ)。

 

じゃあsegwitの何がうれしいかというと、トランザクション展性を抜きにすれば、ブロックに入れるときのデータ量が減らせる、ということだろう。
witnessの部分、P2WPKHなら<signature>と<pubkey>を取り外すらしい。<marker>と<flag>もかな?
これだけで110byteくらいあるから、トランザクションは大きくなるけどブロックに入れるサイズはだいたい小さくなる。
MultiSigみたいに署名が複数あると、効果が大きい。

 

ただ、segwitのトランザクションを作るには、INPUTのところがsegwitになってないとダメだ。
だからP2PKHアドレスの場合は、一度P2WPKHのアドレスを作ってそこに送金し、その次からがsegwitのトランザクションを作ることになる。

nested inだとscriptSigもブロックに入れないといけないので、もったいない。
そうなると、ウォレットアプリも対応しないとなぁ、となってくる。
署名の計算も、前のトランザクションを一部使うことになり、その検証も同じようなことになるので、P2PKHなどよりも面倒だ。


今回書いた内容も、BIPを見れば分かる範囲なのだが、私がsegwitのことを調べ始めたときは、

  • 署名を分離してcoinbaseに置く
  • サイズが小さくなる

くらいで、技術的なところは結局BIPを読むしかなかった。
そしてBIPがわかりづらいのよね。。。英語だし。。。

 

今ではsegwitに対応したBitcoinライブラリもあると思うので、これから始める人はそれを読んでいった方がよいのかも。

2017/04/02

[esp8266]ESP-TOUCHのしくみ (3)

"android udp tethering"などのキーワードで検索すると、port forwardingのことがよく出てきた。
adb forwardコマンドでできると書いてあるので、何も考えずにやってみたが、やはりダメ。

 

android - UDP packets between WiFi-tethered device and WiFi tether provider? - Stack Overflow
単にばらまきたいだけだったのだが、そういえば作りとしては"234.xx.xx.xx"に送信しようとしていた。。。
Androidが取得しているIPアドレスは、"10.xx.xx.xx"みたいな番号なので、マスクで止められたのだろうか?
いや、それだったら、うまくいっているときは"192.168.xx.xx"みたいな番号だから、違うといえば違う。

 

えーっと、IPアドレスのクラスCは、192.0.0.X~223.255.255.Xまでらしい(入社時に購入した古い本より)。
じゃあ、234.xx.xx.xxはクラスCじゃないやん。
その次のクラスDは、224.0.0.0~239.255.255.255とのこと。
この中には入っている。
そして、クラスDはIPマルチキャスト用らしい。

 

5. IPマルチキャスト | TECHSCORE(テックスコア)
MulticastSocketというクラスがあるそうだ。
MulticastSocket | Android Developers
sending and receiving、と書いてあるけど、SmartConfigアプリには使われていないのだよなぁ。
とりあえず置き換えてみよう。
ちっ、DatagramSocketはSocketExceptionなのに、MulticastSocketはIOExceptionなのか。
そして、変更したが動かない。。。

 

http://stackoverflow.com/questions/6550618/multicast-support-on-android-in-hotspot-tethering-mode
setNetworkInterface()で送信対象のインターフェースを指定せよ、という回答が出てきた。
はいはい、やればいいんでしょう、やれば。。。

あ、送信された!

 

じゃあ、あとはBSSIDを取得するだけか。
http://stackoverflow.com/questions/33159224/getting-mac-address-in-android-6-0
うーん、取得はできたが、設定画面に表示されているWiFi MACアドレスと違う。。。
が、動かすと、ESP8266と通信できて、Nexus5のIPアドレス取得はできていないが、自分のIPアドレスはとれているから、うまくいったんじゃなかろうか。

 

使う人はいないと思うが、githubに上げておこう。
AndroidManifestなんかは、ほぼimportしたままになっているので、うまいことやっておくれ。
https://github.com/hirokuma/Esp8266SmartConfigTether

[esp8266]ESP-TOUCHのしくみ (2)

AndroidをテザリングさせてSmartConfigするとうまくいかない件。
ESP8266をSniffer modeにしようかと思ったが、たぶんAndroidからパケットが出てないか、変なパケットが出ているのだろうから、そっちを見ることにした。

困ったときのWiresharkなのだが、Windowsではいろいろ制約があってやりづらい。
VirtualBoxで動かしているLinuxで動かそうと思ったが、NATだと結局Windowsを経由するだろうから、USBドングルのWiFiモジュールを使うことにした。

 

が、ここで問題が。。。
Ubuntuのあるところから、rt8192cuのドライバは修正版を使わないとLinux自体死んでしまうようになっているのだ。
https://github.com/pvaret/rtl8192cu-fixes
別のPC(Native Linux)ではこの手順通りにやればうまくいったのだが、VirtualBoxでやっているためかうまくいかない。。。
dkms installのところで「/usr/src/8192cu/1.10がない」と怒られるのだ。
実際に、ない。
いろいろ試したが、普通にmake & sudo make installし、depmod -aして、blacklistファイルをコピーすればよかった。
困らせやがって。

 

動かしてみると、WiFiのAPに接続しているときはパケットが飛んでいるが、テザリングの時は出ていなかった。
出ていない画像は載せられないので、出ていたときの画像だけ載せよう。

image

AndroidのテザリングとUDPについて調べるべきなのか。。。

[esp8266]ESP-TOUCHのしくみ (1)

ESP8266のESP-TOUCHというか、SmartConfigというか、その辺のしくみを見ていく。
参考にするドキュメントは、これだ。

http://espressif.com/sites/default/files/documentation/30b-esp-touch_user_guide_en_v1.1_20160412_0.pdf

実は、以前も調べたことがあるのだが、深くは追っていなかったのだ。
hiro99ma blog: [esp8266]SmartConfig == ESP-TOUCH


まず、ESP-TOUCHとSmartConfigの関係だが、

  • Espressif's ESP-TOUCH protocol
  • Smart Config technology

ということで、Smart Configというのが方式の名前で、そこで使っているプロトコルがESP-TOUCHということらしい。
Espressif's、とあるので、ESP-TOUCHはEspressif独自だと思って良いだろう。
AirKissというプロトコルも使えるので、Smart ConfigではESP-TOUCHかAirKissが使える、ということになるのか。

今回はESP-TOUCHのみ見ていく。

 

APIは開始と中断、あとはタイムアウト時間設定とバージョン取得くらいか。
プロトコルでどれを使うのかを設定するAPIもあり、開始前に呼んでおくとのこと。
サンプルは設定していないので、デフォルトはおそらくSC_TYPE_ESPTOUCH_AIRKISSだ。

smartconfig_start()すると、あとはコールバックで進んでいく。

image

ESP8266のアプリとしては、SC_STATUS_LINKでwifi_station_set_config()するくらいしかやることがない。
起動してWiFi APと接続できなかったらSmartConfigモードになる、とか、ボタン押したらSmartConfigモードになる、というのが望ましいか。

 

さて、start()すると、ステータスはFIND_CHANNELまでは進むのだが、Nexus5をテザリングさせたままやると、次に進めなかった。
通信できなかったからだとは思うのだが、そもそもどうやってAndroidアプリと通信しているのだろうか?

ドキュメントでは、UDPを使っていると書かれている。
説明図は、こうなっていた。

image

このパケットは、本で調べるとIEEE802.3フレーム形状というもののようだ。
UDPかどうかは"DATA"の部分に載ると思うので、ESP-TOUCHとしてはEthernetパケットに載せているのだろうか?
あるいは、UDPのデータとしてこれが入っているのだろうか。
IEEE802.11標準だとアドレスが4つくらい入っているようだから、データ部に載せる・・・?

いや、Interface誌2012/11月号を読むと、送信側はアドレスとして「自分・AP・相手」を必要とするが、APはそれを受け取って流すときは「自分・相手」だけにしてしまうようだ。
ということは、SmartConfigアプリが自分をAPとして送信する?

そもそも、何も設定していない状態でどうやって希望する無線を見つけ出すのだろうか。
WPSで設定できるのだから、何か手段はあるはずだ。
ただ、なんとなくだが、Androidアプリからそんな深いパケットは制御できないような気がするのだ。
うーーーん。。。。


動かしながらやった方が早そうだ。

おそらく、AndroidアプリのConfirmボタンを押して送信開始するのは、ここだ。
違ってたら、済まん。

https://github.com/EspressifApp/EsptouchForAndroid/blob/master/src/com/espressif/iot/esptouch/udp/UDPSocketClient.java#L97

データを見てみると、0x31をひたすら送っている。
プリアンブルか?
ログでは"send gc code"となっている。ソースのコメントは"send guide code"となっていた。

https://github.com/EspressifApp/EsptouchForAndroid/blob/master/src/com/espressif/iot/esptouch/protocol/EsptouchGenerator.java#L30
gcがguide code、dcはdata codeを指しているようだ。

guide codeを流し続けて、相手が応答したらdata codeを流すのかと思ったが、どうもguide codeとdata codeを交互に流しているようだ。

  • WiFi AP接続中で偽パスワードだと、UDP受信しない
  • WiFi AP接続中で空パスワードだと、UDP受信しない
  • WiFi AP接続中で正しいパスワードだと、UDP受信する

という動きをしたので、ESP8266が応答したらデータを投げるのではなく、とにかくデータを投げ続けて、ESP8266が設定できたら何か送信するので応答できる、という作りのようだ。

 

ということは、テザリングしているときは相手が受け取れていないか、自分がうまく送信できていないかのどちらかだろう。
ドキュメントを読むと、ESP8266はSniffer modeを使っているらしい。
Sniffer modeは使ったこと無いのだけど、無線が出ていたら読み取ってくれるようになってるのではなかろうか。

UDPの送信先は、IPアドレスが"234.xx.xx.xx"(xxはカウント値で毎回変わる)、ポート番号は7001になっている。
これが出ていないのか、受け取れていないのか。
そもそも、テザリングで作ったアクセスポイントをWiFiのアクセスポイントと一緒にして良いものなのか。。。

 

うーん、Sniffer_DEMOを動かしてみた方が良いのか。
長くなってきたので、今回はここで打ち切ろう。

2017/04/01

[android]WiFiテザリング中だったらSSIDを知りたい

ESP8266のSmartConfigを使えるようになっておきたい。
Androidの設定アプリから、今つないでいるWiFiのAPを登録することはできた。
WiFiがない場所でも、Nexus5のテザリングで使えるようにしたいのだが、動かすと<unknown ssid>になり、アプリが死んでしまった。

面倒だが、対応できるのかどうか調べてみよう。


http://tools.oesf.biz/android-6.0.0_r1.0/xref/frameworks/base/wifi/java/android/net/wifi/WifiManager.java#1541

WifiManager#getWifiApState()なり、isWifiApEnabled()なりを使えば、テザリング中かどうかを知ることはできるらしい。
@hideなので今後はわからんが、Android 7.1.1まではありそうだ。
あ、Android7では@SystemApiとかいう修飾が付いてるから、制限されるのかも。
持ってないので知らん(Java reflection is impossible、と書いているから、できんのかも)。

名前からすると、getWifiApConfiguration()で情報がとって来れそうだが、SmartConfigアプリと違ってWifiConfigurationを返す。
まあ、SSIDだけわかればよいから、問題なかろう。

幸い、reflectionしたサンプルがstackoverflowにあったので、使わせてもらう。
http://stackoverflow.com/questions/10850936/android-tether-get-current-ssid


テザリングしていたら、という条件を入れようかと思ったが、今のところ「<unknown ssid>」という文字列になっているから、その場合だけWifiApから取ってくることにした。

 

Javaが、ハードTABでインデントしているので、置換。
これでたぶんできたんだと思う。自信はないが。。。

$ find ./ -name "*.java" -exec sh -c 'expand -t 4 {} > $$$ ; mv $$$ {}' \;

 

よし、と実行すると、SSIDはテザリングの設定画面で入力していたものが表示された。
よしよし、と進めると・・・やはり死んでしまった。
どうやら、BSSIDがnullだったためのようだ。
無線LAN - BSSIDとESSID
通常は、BSSIDはMACアドレスと同じ、ということだったので、Nexus5の端末情報からWiFi MACアドレスを見て、まずはコード埋め込みにして動作確認することにした。

うん、BSSIDを入れておくと、死ぬことはなくなった。
が、ESP8266がNexus5を見つけられないのか、コンソールには何も出ない。

 

なんとなくだが、SmartConfigはWiFiクライアント同士が通信するようなものになってるのではないだろうか?
だからAPになったNexus5からはうまくいかない、と。

面倒だが、SmartConfigがどういうしくみなのかを調べる段階に来てしまったようだ。