2018/07/28

[c/c++][mbedtls]ChaCha20-Poly1305サポートとIETF variant (3)

第3弾まで書く気はなかったのだが、ネタは思いついたときに残しておかねば。



libsodiumからMbedTLSに全部置き換えたいと思っている。
別にlibsodiumに不満があるわけではないのだが、使っている理由がChaCha20とPoly1305だけなので、MbedTLSが対応したなら統一したいのである。


ChaCha20-Poly1305については、IETF variantのAPIを使っていたため、MbedTLSと置き換えできた。
しかし、見逃していたのだが、別の箇所でChaCha20を使っていることに気付いた。
こっちは純粋にChaCha20単体で使っていたのだが、nonceが8byteになっているからオリジナルのChaCha20っぽい。


さて、困った。
MbedTLSはChaCha20単体もIETF variantっぽい(nonceが12byteになっていたから)。

もしかしたら同じ結果になるかもしれない、と思ってやってみると、あら不思議、同じになるではないか。
しかし、条件があるに違いない。


libsodiumのChaCha20処理は、このあたりにある。
https://github.com/jedisct1/libsodium/tree/0468e778d229054a966d0fdc629ec8b677bc37c6/src/libsodium/crypto_stream/chacha20


呼ばれているのは、これのようだ。
_ietfが付いているかどうか。
https://github.com/jedisct1/libsodium/blob/bac61ebf506c1b32185a3f3cc6a582445d55bc75/src/libsodium/crypto_stream/chacha20/ref/chacha20_ref.c#L226-L262

この2つの違いはchacha_ivsetup()かchacha_ietf_ivsetup()かだけみたいだ。


IV設定は、ここ。
https://github.com/jedisct1/libsodium/blob/bac61ebf506c1b32185a3f3cc6a582445d55bc75/src/libsodium/crypto_stream/chacha20/ref/chacha20_ref.c#L62-L78

counterのバイト数と、ivのバイト数が違うだけのように見える。


RFC-7539の7ページ目に図が載っている。
https://tools.ietf.org/html/rfc7539#page-7

image

IETF variantは、bが1つ、nが3つになっている。
ということは、オリジナル版は、bが2つ、nが2つということだろう。
それなら、IV設定の関数と整合がとれる。


ということは、これを利用して、MbedTLSでもcounterとnonceの配分をうまくやればいけるのだろう。
stream()ではcounterがNULLになっているから、0扱い。
nonceは8byteだから、何も考えずに置き換えて使えるんじゃなかろうか。
エンディアンがちょっとわからんのだが、必要があれば4バイトシフトさせればいいだけだ。


私が置き換えたかったのはcrypto_stream_chacha20()だったので、前回と同じようにソースを書いてみた。

https://gist.github.com/hirokuma/f9ee0da7bd428458662473964b57fa6c

nonceの前方に4byteの0を入れると同等になるみたいね。

[c/c++][mbedtls]ChaCha20-Poly1305サポートとIETF variant (2)

前回の続きだが、あまり大したことは書けない。

  • MbedTLSにChaCha20-Poly1305がサポートされたけどIETF variantなのか?
  • ソースファイルからするとIETF variantっぽいし、libsodiumのIETF variant APIで同じ結果になった
  • じゃあ、なんで私がやったAPI置き換え版は結果が同じにならないの??


はい、なんとなく予想できたかと思うが「置き換えに失敗していたから」でした・・・。



これだけだとあんまりなので、どう間違えたのか書いておこう。

libsodiumは、エンコードしたデータ+MACをまとまったデータとして扱っていたのだ。
しかし、MbedTLSでは別々に扱っている。
だから置き換える際に、MACのアドレスも指定するし、データ長もMACを外したサイズにせねばならなかったのだ。

そこら辺を間違えていたのですな。
まあ、ありがちといえばありがちだが。

[c/c++][mbedtls]ChaCha20-Poly1305サポートとIETF variant (1)

Mbed TLS 2.12.0, 2.7.5 and 2.1.14 released


いやあ、とうとうリリースされました。

私にとって大きいのは、ChaCha20-Poly1305サポート。
だいたいの部分はMbedTLSで実装していたのだが、ChaCha20-Poly1305だけはlibsodiumを使っていたのだ。
libsodiumを別の環境でビルドするのは難しそうだったのだけど、MbedTLSなら何とかできる気がする!



で、APIを置き換えてみたものの・・・結果が違う。
全部じゃなくて、MACのチェックで失敗するものがあるのだ。


何も考えずに置き換えていたのだが、libsodiumは3種類APIがあるのだ。

Authenticated Encryption with Additional Data using ChaCha20-Poly1305

  • The original construction can safely encrypt up to 2^64 messages with the same key (even more with most protocols), without any practical limit to the size of a message (up to 2^64 bytes for a 128-bit tag).
  • The IETF variant. It can safely encrypt a pratically unlimited number of messages, but individual messages cannot exceed 64*(2^32)-64 bytes (approximatively 256 GB).
  • The XChaCha20 variant, introduced in libsodium 1.0.12. It can safely encrypt a practically unlimited number of messages of any sizes, and random nonces are safe to use.

私が使っていたのは、この真ん中のIETF variantというAPI。
ソースファイルを見比べたのだが、originalのものに似ているが、追加されているような感じがする。

MbedTLSでも同じことができるものかは別として、IETF variantが何なのかを調べておこう。


"variant"は「変形」とか「変種」というような意味。
だから、originalに対してIETFなりの変更を加えたバージョンということだろう。

libsodiumのIETF variant APIページの一番下にリンクがあった。

ChaCha20 and Poly1305 for IETF protocols

IETF variantの仕様らしい。
リンク先は、RFC-7539。

あれー、と思うだろう。
だって、MbedTLSのソースファイルにもRFC-7539と書いてあるのだから。。。



むう、もしかして、APIが違うんじゃなくて、置き換え損なっているのだろうか?
APIが同じ種類かどうかは、テストデータを動かして確認するしかあるまい。



https://gist.github.com/hirokuma/e8d3e68300810d284b7d65df5473fe22

データをMbedTLSのtestから持ってきて、APIはlibsodium 1.0.16で実装。
困ったことに、"ok"が出力されてしまった。


なんで困ったかというと、encryptのテストしかないからだ。
失敗していたのはMACのチェックなので、どちらかといえばdecryptの方なのだ。

これは、もっとちゃんと調べろ、ということなんだろう。

2018/07/21

[git失敗]最新のpushから1つresetして編集

gitで起こしてしまった、現在進行形のお話。


別の場所で作業するので、一旦pushした。
かなり適当な内容なので、pushよりはファイルごとコピーしておきたいくらいだったのだが、面倒になったのでpushした。


で、今見直したところ、あまりにもいい加減な変更だったので、前のcommitは止めようと思った。
が、そのままではダメだけど、書き換えれば使い物にならないわけでもなさそうだったので、差分を見ながら書き換えようとした。
私はvscodeを使っているのだが、差分が見やすいので助かるのだ。


そして、何も考えずにこうやった。

$ git pull
$ git reset --soft HEAD^

そうすると、ソースファイルはpushしたものが残っているが、差分としてはまだ反映されていないような形になるので、最後に変更した箇所が見える。


無事に変更を終え、push。

$ git push

error: failed to push some refs to 'git@github.com:xxxx.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Integrate the remote changes (e.g.
hint: 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.

エラーらしい。
resetしたからだろう。

$ git pull

CONFLICT (content): Merge conflict in xxxxxxx
Automatic merge failed; fix conflicts and then commit the result.

コンフリクトした。

そして困ったことに、オートマージされたので、前回削除したり追加したりしてコンフリクトしなかったところが反映されてしまったのだ。

「gitでやったらいかんこと(その1)」というところか。。。



gitだけで解決できそうだけど、調べるのが面倒だったので、手動で対応させることにした。


まず、今のコンフリクトしたファイルを適当に修正して、別の場所にコピー。
そして、ローカルでやった修正をなかったことにするため、git reset --hard HEAD^。
pushした内容に戻すため、git pull。
そして、その修正はいらないので、git revert HEAD。

あとは、移動させたファイルとオリジナルのファイルをWinMergeで比較しながら手動マージ。
アナログじゃろう?


今回はコンパイルエラーが出たので気付いたが、もっとぼーっとしていたら、オートマージに気付かなかったかもしれない。


さて、今回調べたいのは、どうするのが楽だったのか、だ。
やりたかったのは「最新pushを1つ前に戻しつつ、最新pushのソースも見ながら編集したい」だ。


うん、そうだね。

$ git pull
$ 差分を見たいソースファイルをどこかにコピー
$ git revert HEAD^
$ 差分を見ながら編集

ファイルをどこかに置けばよかっただけか。


ただ、vscode+GitLensだと、履歴と比較する機能がありそうだ。
Working Treeとも比較ができるので、次回はそれを使おう。

image

2018/07/09

[java]64bit値を扱うのにBigIntegerもある

Cからもらったunsignedな64bit値をビット演算するだけでよいかと思っていたら、数値としても扱う箇所があるのに気付いた。

うーん、uint64_tはプリミティブだから、Javaもlongで受け取って、中で数値に変換できないものか。。。


調べていると、BigIntegerというものがあった。
これを使って、引数はlongで受取り、中で16進数文字列に置き換え、それをBigIntegerにすればよさそうだ。

https://gist.github.com/hirokuma/f365c2a85dae3962ff3d5f19562b9029/a0e48bef4148691ce3fcc135c9911662ce21afc3

結果

long: -81985529216486896
long 2byte: -2
BigInteger: 18364758544493064720
BigInteger 2byte: 254

意味も無くビットシフトなんかさせてみたが、ちゃんと正の数として扱っている。

文字列は好きではないのだが、C側でやらなくてよいので、まあよかろう。

2018/07/07

[java]uint64_t相当のデータをビット演算したい

JNIを使って、CとJavaをつなごうとしている。
C側でuint64_tを使っている箇所があるので、Javaで何とかしなくてはならん。


Javaのlongは64bitなのだが、Javaにはunsignedがないので、たぶん最上位ビットが立ってたら負の数と思うだろう。
byteもそうだったし。

確認しておくべし。


longだけではなく、BigDecimalも使ってみよう。

https://gist.github.com/hirokuma/c8a67b1db8e44ce45236942088d81865/1aaa1e1893b3f83f9d1d14d494b1d4902da454ee

同じようなことをさせたいが、BigDecimalにビットシフトがないので、割り算で代用。
処理が遅くなりそうだが、まあ動作確認だからな。

結果

big decimal 2byte: fe
long 2byte: fffffffffffffffe

やはり、そうなるのか。
BigDecimalは演算してるから2byte分きっちりになったが、longはビットシフトで符号が引きずられたんだ。


そういえば、Javaには「>>>」があったな。

https://gist.github.com/hirokuma/c8a67b1db8e44ce45236942088d81865/0161393d1d388b1ad97754dbc84d3efb2eff8370

結果

big decimal 2byte: fe
long 2byte: fe

おお!
なんか、うれしくなりますな。


これはHEXで出しているから、変数として正負どちらになっているかわからん。
数字で出力させて確認。

BigDecimal : 正
longで>>:負
longで>>>:正

まあ、予想通りか。
当然だが、これをbyte型にキャストして代入すると、どれも負の数になる。