はちゅにっき

こっちのブログはまったり更新

Perl と JavaScript 間で RSA 暗号を使いたい

サンプルが見つからないってことは、普通はやらないことをやろうとしているんだと思う。
ということで、以下のようなことをしてみたかったので、試してみました。

  1. Client (JavaScript) 側で RSA 鍵 (秘密鍵・公開鍵) を生成
  2. 公開鍵を Server (Perl) に POST
  3. Server (Perl) 側は受け取った公開鍵を用いて暗号化し送信
  4. Client (JavaScript) は受け取った暗号文を 1 で生成した秘密鍵で復号

とりあえずサンプルで作ったものは

  1. RSA 鍵を生成 (Client)
  2. 公開鍵を POST (Client)
  3. UUID を生成 (Server)
  4. POST された公開鍵を使い暗号化 (Server)
  5. 受信した暗号文を秘密鍵で復号 (Client)

という動作をします。
できたもの、ソースコードはこちら。

JP RSA
http://jprsa.magicalhat.jp/
hatyuki/p5-jprsa
https://github.com/hatyuki/p5-jprsa

以下雑文

続きを読む

HTML5 の canvas をさわってみた

仕事をしている時、ちょっと QR コードが欲しくなったけれど、手軽に生成できるサイトが見つけられなかったので、HTML5 の練習も含めて作ってみることにしました。
作ったのはこんな感じ。

Zebra
http://qr.magicalhat.jp/
github
https://github.com/hatyuki/Zebra

テキストエリアに何か入力すると、それをリアルタイムで QR コードに変換してくれます。
たとえば般若心経を入力するとこんな QR コードが生成されます。誰得なんでしょうね。
って、何の変哲もない QR コードなんだけど。。。
f:id:magicalhat:20130405010237p:plain

続きを読む

jperl と jcode.pl から卒業する

何をいまさらな話題ですが、文字コードEUC-JP が主流だった時代に作られた jperl と jcode.pl に依存しているレガシーなシステムをモダンな Perl に書き換える場合には、Encode に加えて Encode::EUCJPMS を使うと簡単でした。
というメモ。

Encode-EUCJPMS
http://search.cpan.org/~naruse/Encode-EUCJPMS/

これで機種依存文字を恐れず処理することができました。

Unicode にさえなってしまえば後はやり放題で、日本語の「全角←→半角」変換などは Lingua::JA::Regular::Unicode が便利でした。

Lingua-JA-Regular-Unicode
http://search.cpan.org/~tokuhirom/Lingua-JA-Regular-Unicode/
use utf8;
use Encode::EUCJPMS;
use Encode ( );

# eucJP-ms な DB から値を取得して
my $row = $sth->fetchrow_hashref( );

# Perl の世界に decode して
my $utf8 = Encode::decode('eucJP-ms', $row->{japanese});

...
# なんか処理して
...

# eucJP-ms で戻してあげる
$dbh->execute(Encode::encode('eucJP-ms', $japanese));

こんな感じ。
ちなみに、今回は DB からいろいろ取得して、文字列処理をして DB に戻す。という、だいたい完了まで 30分くらい処理にかかるようなスクリプトを書き直したのですが、書き直した後も実行時間は特に変わらず *1 Encode でいったん EUC-JP から Unicode に変換し、再び EUC-JP に戻すこと自体はパフォーマンス的なマイナスはないと思います。
むしろ jperl から脱却することによるメリットの方がおおきいですね。
これで「触らぬ Script に障害なし」とか言う謎の遺言からも解放です。
わーい。楽ちんですね。

*1:実際は 5分くらい早くなった

WebSocket を使って Amon2 でチャットプログラムを書いてみる

ずいぶん前ですが Amon2 が WebSocket に対応したということで、今更感のあるチャットプログラムを書いてみることに。
今更とは言いつつも社内では IRC を使うことができないので、ちょっとしたメッセージがやりとりできるものを作ってみようかなと思い、そのベースとなるようなイメージで作ってみました。
オマケで以前から使ってみたいなぁと思っていた Redis や Proclet なんかも (無駄に) 使ってみました。
適当な Heartbeat で WebSocket を維持していたり、エラー処理が適当すぎて Member の一覧が嘘つきだったり *1 と、まだまだイケてない部分ばかりですが、最近時間がなかなか取れないこともあり、とりあえず動くし晒してみようかと思います。
Twincle の名前は昔テストで Twitter Client を作ったときに押さえた ID をそのまま踏襲しただけで、特に意味はないです。
README すらないご覧のザマだよ。

hatyuki / twincle
https://github.com/hatyuki/twincle
デモ
http://twincle.magicalhat.jp/

セッションで Socket を管理しているため、同一ブラウザで2つウィンドウ立ち上げてもうまく動きません。この辺は Hash::MultiValue をうまく使えばいいのかなー。などと検討中。

なんとなく工夫した気がするところ

WebSocket ではバイナリデータを送受信することができるので、今回は Perl (Amon2) ←→ JavaScript 間を MessagePack でバイナリ化してやりとりをしてみました。
Amon2 では下位のレイヤーでは Protocol::WebSocket を利用して WebSocket のやりとりをしていますが、その部分は見事に隠蔽されており、プログラムするときは Protocol::WebSocket の存在を意識せずに、文字列を単に渡すだけで WebSocket を扱うことができます。

  • MyApp::Web::Dispatcher
get '/socket' => sub {
    my $c = shift;

    $c->websocket( sub {
        my $ws = shift;
        $ws->send_message("文字列を渡すだけで OK");
        # ...
    } );
};

が、バイナリデータを送受信するためには Protocol::WebSocket::Frame のコンストラクタで送信したいフレームがバイナリデータであることを指定する必要があるため、ちょっと都合が悪いこととなります。
そこで、Amon2::Plugin::Web::WebSocket をちょっとだけ編集して、文字列だけではなく Protocol::WebSocket::Frame そのものも渡せるようにしました。

  • MyApp::Plugin::Web::WebSocket
# ... (中略)
$ws->{send_message} = sub {
    my $message = shift;

    # Protocol::WebSocket::Frame オブジェクトを受け付けるように
    unless (eval { $message->isa('Protocol::WebSocket::Frame') }) {
        $message = Protocol::WebSocket::Frame->new($message);
    }

    $h->push_write($message->to_bytes);
};
# ... (中略)

これにより以下のように、バイナリフレームを送信することができるようになります。

  • MyApp::Web::Dispatcher
get '/socket' => sub {
    my $c = shift;

    $c->websocket( sub {
        my $ws = shift;

        # バイナリフレームを作成して
        my $frame = Protocol::WebSocket::Frame->new(
            type => 'binary',
            buffer => $binary_strings,
        );

        # 送信
        $ws->send_message($frame);
        # ...
    } );
};

無駄にハマって解決できていないところ

手元の Mac では再現しませんが、Debian 上で実行するとなぜか LWP::UserAgent がすごい時間がかかる。
具体的には不自然に必ず30秒以上かかる。なんかタイムアウトを待っているみたいな。。。
そのせいで Net::Twitter::Lite を使った Twitter の認証に必要以上の時間がかかってしまい、困ったことになりました。
とりあえずの対処として、Net::Twitter::Lite が LWP::UserAgent ではなく Furl を使うように無理矢理パッチをあてました。

package Twincle::Auth::Site::Twitter;
use strict;
use warnings;
use Furl;
use Net::Twitter::Lite;

sub import
{
    no warnings 'redefine';

    # "default_header" メソッドがないとエラーになるので申し訳程度に追加
    *Furl::default_header = sub { };

    my $orig = *Net::Twitter::Lite::new{CODE};
    *Net::Twitter::Lite::new = sub {
        my ($class, %args) = @_;

        # UserAgent を LWP::UserAgent から Furl に差し替え
        $args{ua} = Furl->new;
        $args{legacy_lists_api} = 0;

        $orig->($class, %args);
    };
}

1;

LWP::UserAgent が遅い原因については据え置き中。
時間があるときに調べてみよっと。

まとめ

Amon2 の WebSocket サポートを使うと、すごく簡単に WebSocket を使うアプリケーションが作成できました。
バイナリデータを送受信するために改良した部分は、もう少し確認して本家の方へ pull request してみたいなぁと思います。
まだまだ追加したい機能はたくさんありますが WebSocket や Redis など、使ってみたかったものを使いつつ動くということで
わーい。

*1:きちんとログアウトしないと幽霊部員になる

Net::SSLeay がインストールできないときは

OS って 1回セットアップすると、あまり触らなくなるから、すぐ忘れちゃうんだよね。。。

cpanm などで Net::SSLeay をインストールしようとして "openssl/err.h" がないよ!と怒られたとき*1

# aptitude install libssl-dev

*1:SSLeay.xs:153:25: fatal error: openssl/err.h: そのようなファイルやディレクトリはありません

GrowthForecast つかってみた

なんかすごくグラフを作成したい要件があったので、id:kazeburo さんが開発している GrowthForecast をつかってみました。

GrowthForecast
https://github.com/kazeburo/GrowthForecast

いつも通りメモだよん。

GrowthForecast を Clone する

App::gh *1 をインストールしてあるので、gh コマンドを使って

$ gh clone kazeburo/GrowthForecast

でおわり。便利ですね!

依存モジュールをインストール

cpanm さえ入っていればこれもいつものように

$ cpanm -l extlib --installdeps .

でおわり。これまた便利!

Alien::RRDtool をインストール

依存モジュールインストールしたし、もうカンペキ!と思ったけれど、実はグラフ描画ツールである RRDtool は一緒にインストールされない模様。
ということで、RRDtool も導入します。
今までは yum / aptitude などを経由してインストールしたり、自分でコンパイルしたりと大変でしたが、今は Alien::RRDtool があるので、こちらを cpanm を使っていれます。
依存するライブラリの問題でインストールできない場合もありますが、その場合は yum / aptitude などで依存ライブラリを別途インストールする必要があります。

$ cpanm -l extlib Alien::RRDtool

起動してみる

GrowthForecast には growthforecast.pl という起動スクリプトがついてくるので、これを実行してアプリケーションを起動します。

$ perl growthforecast.pl --host 0.0.0.0

これだけで準備完了。カンタン!

curl でグラフをかく

特定の URL を叩くだけでグラフを生成してくれるので、ためにし curl でグラフを書いてみます。

$ curl -F number=2.5 -F mode=gauge http://mydomain.com:5005/api/service/section/name

これだけでカンタンにグラフが。。。
あれ?なんか "number is not null" とか言うエラーが出ちゃったよ?
NULL 値なんて渡してないのにどうして!?
と思い、ソースコードを見てみると、どうやら number は整数値じゃないとダメみたい。

整数値以外のグラフも描画できるように

今回どうしても、整数値以外のグラフが書きたかったので、無理矢理整数値以外でも描画できるようにしてみました。
変更した差分は以下の通り。

diff --git a/lib/GrowthForecast/Data.pm b/lib/GrowthForecast/Data.pm
index f334efa..c88c24a 100644
--- a/lib/GrowthForecast/Data.pm
+++ b/lib/GrowthForecast/Data.pm
@@ -27,7 +27,7 @@ CREATE TABLE IF NOT EXISTS graphs (
     service_name VARCHAR(255) NOT NULL,
     section_name VARCHAR(255) NOT NULL,
     graph_name   VARCHAR(255) NOT NULL,
-    number       INT NOT NULL DEFAULT 0,
+    number       REAL NOT NULL DEFAULT 0,
     mode         VARCHAR(255) NOT NULL DEFAULT 'gauge',
     description  VARCHAR(255) NOT NULL DEFAULT '',
     sort         UNSIGNED INT NOT NULL DEFAULT 0,
@@ -60,7 +60,7 @@ EOF
     $dbh->do(<<EOF);
 CREATE TABLE IF NOT EXISTS prev_graphs (
     graph_id     INT NOT NULL,
-    number       INT NOT NULL DEFAULT 0,
+    number       REAL NOT NULL DEFAULT 0,
     subtract     INT,
     updated_at   UNSIGNED INT NOT NULL,
     PRIMARY KEY  (graph_id)
@@ -70,7 +70,7 @@ EOF
     $dbh->do(<<EOF);
 CREATE TABLE IF NOT EXISTS prev_short_graphs (
     graph_id     INT NOT NULL,
-    number       INT NOT NULL DEFAULT 0,
+    number       REAL NOT NULL DEFAULT 0,
     subtract     INT,
     updated_at   UNSIGNED INT NOT NULL,
     PRIMARY KEY  (graph_id)
@@ -83,7 +83,7 @@ CREATE TABLE IF NOT EXISTS complex_graphs (
     service_name VARCHAR(255) NOT NULL,
     section_name VARCHAR(255) NOT NULL,
     graph_name   VARCHAR(255) NOT NULL,
-    number       INT NOT NULL DEFAULT 0,
+    number       REAL NOT NULL DEFAULT 0,
     description  VARCHAR(255) NOT NULL DEFAULT '',
     sort         UNSIGNED INT NOT NULL DEFAULT 0,
     meta         TEXT NOT NULL DEFAULT '',
diff --git a/lib/GrowthForecast/Web.pm b/lib/GrowthForecast/Web.pm
index 61539e9..6dd813f 100644
--- a/lib/GrowthForecast/Web.pm
+++ b/lib/GrowthForecast/Web.pm
@@ -748,7 +748,7 @@ post '/api/:service_name/:section_name/:graph_name' => sub {
         'number' => {
             rule => [
                 ['NOT_NULL','number is null'],
-                ['INT','number is not null']
+                # ['INT','number is not null']
             ],
         },
         'mode' => {

ValidateRule とか、HTML::Shakan を使ってそうな書き方なので、本当は数値だけが通るようにとかしたほうがいいとは思うんだけれど、とりあえずこれで整数値以外の値でもグラフがかけるようになりました。
いまのところ特に問題はないし、まぁいいかな?


というわけで当初の目的通り、グラフを生成することができました。

後日談

本当は GrowthForecast ではなく、HRForecast を導入しようとしていて、MySQL ではなく PostgreSQL にも対応できるように書き換えたりとか頑張ったけれど、最終的に HRForecast も整数値しかグラフにすることができなく、その部分を直すのがめんどくさそうだったので GrowthForecast に乗り換えました。時間があるときに HRForecast 整数値以外でグラフがかけるようにしたいな。

*1:cpanm App::gh でインストールしました。

Apache Killer をかいてみる

すっごい久しぶりに日記を書こうかと。
Apache Killer が騒がれてから、ずいぶん時間も経ったのでちょっと前のネタを投稿。

「うちのサーバも Apache Killer に対処しなきゃね。」

と騒いでいた頃に、検証用のために Apache Killer をダウンロードしてみたんだけれど、攻撃手段 *1 がスクリプト内にハードコードされていて、検証するたびにその部分を書き換えなきゃならなくて、ちょっとメンドクサイ。
ということで

Perl の勉強をしつつ、ちょっとモダンな Apache Killer を作ってみよう。」

という流れで作ってみました。不純な動機ですね。
工夫したところは。。。

  • Getopt::Long であらゆる項目を引数で受け渡せるように
  • HTTP リクエストを Furl を使う
  • FurlX::Coro で Coro 駆動にした
    • Parallel::ForkManager で Fork も可能

あんまりキレイには書けてないんだけど、とりあえず FW の機能などを検証するには充分な感じになりました。
あくようげんきんだよ!

*1:スレッド数や、リクエストのレンジ数