ジャンル不定の日記です。

コマンドラインでBase64のSHA1ハッシュを出力する方法

先日からOrange Pi R1の管理用CGI関連を暇な時間にやってて、とりあえずHTTP+CGIでできるのは公開もして一段落してるが、
websocketの実験も兼ねてsocat+shでwebsocketサーバー作ってる。

WebSocketの接続は
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Origin: http://example.com
Sec-WebSocket-Version: 13
リクエストがこんな感じで、
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
レスポンスはこんな感じになるみたい。

ここで重要なのは、
リクエストのSec-WebSocket-Keyを元にSec-WebSocket-Acceptを作る。
作り方は、
Sec-WebSocket-Keyの値に"258EAFA5-E914-47DA-95CA-C5AB0DC85B11"を追加した文字列のSHA1ハッシュをBase64した文字列がSec-WebSocket-Acceptになる。
なので、
"dGhlIHNhbXBsZSBub25jZQ==258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
を変換して
"s3pPLMBiTxaQ9kYGzzhZRbK+xOo="
にできればいいんだが、
sha1sumコマンドにはBase64を出力する機能もバイナリを出力する機能もない・・・

で調べたが、
$ echo -n "dGhlIHNhbXBsZSBub25jZQ==258EAFA5-E914-47DA-95CA-C5AB0DC85B11"|openssl dgst -binary -sha1|base64
s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
sha1sumを使わずにopensslコマンドを使う。
$ echo -n "dGhlIHNhbXBsZSBub25jZQ==258EAFA5-E914-47DA-95CA-C5AB0DC85B11"|sha1sum|xxd -r -p|base64
s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
↑xxdコマンドでsha1sumの16進文字列をバイナリに変換する。
$ echo -ne "$(echo -n "dGhlIHNhbXBsZSBub25jZQ==258EAFA5-E914-47DA-95CA-C5AB0DC85B11" | sha1sum | cut -f1 -d" " | sed -e 's/\(.\{2\}\)/\\x\1/g')" | base64
s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
↑sedを使ってるがわけわからんコードw
の3種類の方法を発見した。

opensslはBuildrootの標準で入ってない。
xxdは母艦のArch Linuxに無いから
Buildrootにもないかと思ったがあった!が、-rオプションに対応していない・・・
sed使うコードはわけわからん。
というわけで考えたが、openssl入れてまで実現したくないな・・・
なんだが、websocketの実験もしたいし、とりあえず母艦で実験は続けるか。

てなところで記事書いたのだが、
sed使う
$ echo -ne "$(echo -n "dGhlIHNhbXBsZSBub25jZQ==258EAFA5-E914-47DA-95CA-C5AB0DC85B11" | sha1sum | cut -f1 -d" " | sed -e 's/\(.\{2\}\)/\\x\1/g')" | base64
↑このコード、よく見るとわかるな。
cut -f1 -d" "
はsha1sumはハッシュ値とファイル名をスペース区切りで出力するんでハッシュ値だけを取り出すのに使う。
sed -e 's/\(.\{2\}\)/\\x\1/g'
は2文字ずつ取り出して手前にエスケープ文字をつける。
カッコで囲んで先頭の-eオプション付きのechoで出力してるが、echoの-eオプションはSTDINには対応できないから無名変数みたいな変なコードになってるんだね。
このコードキモいが、シェルスクリプトで書くなら一旦変数に入れて2行にすれば普通に書けるね。
なので、xxdもopensslもないならsed使う方法がいいね。

あと、WebSocketサーバーをshで実現するのに使おうとしているsocatコマンドもBuildroot標準では入ってないんだけど、このコマンド便利かも。
ソケット・ファイル・コマンド等から入力して、別の
ソケット・ファイル・コマンド等に出力できる。
なので、これ使えばshでサーバー作れちゃう。
類似のでnetcat(nc)コマンドってのもあるようだが、これは
コマンドにパイプで直接渡すことはできないみたいで、使ってる人の情報見るとfifoで中継してたりする。
netcatはBusyBoxの機能で有効にできるみたいだが、Buildroot標準で入ってないからnetcat入れるならsocatのが良さげ。