2013/12/26

ProTools

プロなら道具にこだわるのは普通だろうという話。
 職場にはRealForceだのHHKだのド級のキーボードを使ってる人も居れば、お馴染みのDell製キーボードのままの人も居る。Dell製キーボードも新しいなら使い心地悪く無いので別に良いと思う。しかし同僚のところに行ってちょいちょいとPC操作するとキーが明らかに固いので閉口する。安いキーボードは油?が切れるとギチギチになってしまう。
 たまにPC操作するような事務系の仕事の人なら仕方ないと思うが、プロのエンジニアがこんなことで良いんだろうかと思ってしまう…。寿司屋に入ってみたら板前さんが100均の万能包丁でギチギチと切り難そうに魚を捌いていたら、絶対マズイ店だと思うだろう。商売道具、という言い方がある。プロとして仕事をする人が道具にこだわるのは当然のことなんだけど何かこの世界ではあまり重要視されてない。ちゃんとした道具を支給してくれない会社も悪いとは思うが…。
 RealForceとかは別として数千円も出せば別格のキーボードが買える時代、ギチギチのキーボードのまま仕事してるなんて、真剣にこの仕事やってないのかなと思ってしまうんだが。なんていうか、高級寿司屋に修行に来たと思ったら先輩がみんな100均の万能包丁使ってたって感じで、悲しいよね。まぁ確かに悪いキーボードで打ったプログラムは動作が遅いなんてないけどさ。

 頭の固いことを言うと、支給品から私物へキーボード、マウスを変える際、1つだけ注意していることがある。「仕様は支給されたものと同じもの」。つまり、キーボードは日本語106/109配列、テンキーのあるフルサイズのもの。マウスはスクロールホイール付きの…、まぁ普通の奴。これはエンジニアとしてじゃなく会社員としてのこだわり。何か緊急で他人が自分のPCで操作することが必要になった際にスムーズに作業できるようにするため。これなら文句言われないだろうという目論見(てか実際職場の人は、やれ親指シフトだのファンクションキーすら無いHHKだのトラックボール式マウスだの、自由過ぎだろw)。

 包丁とか鋏とかを使う職人の場合、ウン万円のプロ用でなければいけない気がするが、マウスキーボードなんてのはそこそこでも十分なもんだ。実際今使ってるのは4000円のキーボードと2000円のマウスだし。(他の分野の職人の商売道具がどの程度の物なのか興味深い。包丁はいかにも高そうだと思っていたが、まさか鋏がこんなにとは…。)

 忘れてたけど大事なことは打鍵音。小心者なのでオフィスに自分の打鍵音が響くと気になってしまい集中力が途切れる。他人のはあまり気にならないんだけどね。 いろいろなキーボードを使ってきたがここ最近はパンタグラフ式のストロークの浅いものに落ち着いた。ノートPCと同じパチャパチャ系のやつ。ノートPCを使う時間が長かったのでそれに慣れているというのもあるが、明らかにストロークが浅い方が静かで速くて快適に打てると思うな。高級品は大抵ストロークが深いんだけど…。

エンドユーザに対する電話サポートが必要な場合もキーボードが標準的なのでないと困る。
デザインのために印字を省略しているキーボードを使っていたんでは、「-」(ハイフン)を出すためにとっさに「キーボード右上、ひらがなの”ほ”のキーを探してください」みたいな的確なアドバイスが出来ないのだ。

2013/12/21

Wolf Children Ame and Yuki

おおかみこどもの雨と雪」を見ました。もちろん金曜ロードショーで。
残念ながら9時半まで「天空麻雀」を見てました。亀仙人のような風貌が好きでファンの灘麻太郎プロが勝っていたので最後まで見てしまった。いつ見ても勝ってるヒサトプロが珍しく一人で沈んでたのでそのせいもある。面白い対戦だった。
 さておおかみこどもは30分遅れで見始めたが無難ではあるものの良かった。この先この親子はどうなってしまうのか気になって、飽きないしファンタジー作品として優秀だと思う。そしてアニメ映画において声優さんはやっぱり重要だ。素人っぽい声優さんが出てるだけで見るのが苦痛になってしまう(サマーウォーズもテレビで見ようとしたけど、台詞1つでチャンネル変えさせるほどの破壊力があった)。ところどころ海外輸出を意識してるようなシーンが差し込まれているがやはり海外映画祭でも賞を取っているらしい。
まぁエクセレント!とかファンタスティック!ではないんだけど、ノットバット、な感じ。

感染列島」見ました。BSでやってたので。
なんでこんなの見ちゃったかなぁ。ひどい映画でした。
アウトブレイクで見たなぁというシーンが沢山。リーダーの女医さん、あのタイミングで新しい治療法を試すって、自分だけ助かりたい人に見えちゃって面白かったがw
毎回酷評する時はちょっと気が引けるんだけどAmazonレビュー見たら「かつてない駄作か」「最低最悪の一本」などと書かれていて安心した。やっぱそう思うよねぇw

2013/12/20

DSA署名をC++で作る(概要)

そもそも署名とは、とある文書が作成当初から改ざんされていないことを保証するためのものです。「平文」だけで渡してしまうと、改ざんされていても全く分かりません。しかし例えば「平文+平文の文字数」で相手に渡すと相手は文字数を数えて文字が増えたりしてないか(改ざんされていないか)確かめることが出来ます。もちろん文字数では帳尻合わせ放大なのですが、DSA署名はそれの非常に強力なやつです。
 全体イメージです。RSA暗号のように秘密鍵で署名を付け、公開鍵で署名を確認してもらう方式です。
上記図でGeneratorが平文のファイルに署名を付けるモジュールです。Generatorは秘密鍵を使って署名を付けます。Generatorや秘密鍵は絶対に外に持ち出しません。
相手にはVerifierと署名付きの平文だけを渡します。署名に公開鍵が入っているのでVerifierは平文と公開鍵を使った計算を行いOKかNGか求めます。
 署名を付けるには秘密鍵が必要だが相手にはそれを渡さない、というのがポイントです。
 つまりこういう状況です。ある箱の鍵を掛けるのはS〇nyにしか出来ない。しかし開けることは誰にでも出来る。ということはその箱に鍵がかかってるなら、箱の中身はS〇nyが用意したものに違いない。逆に開いているなら誰かが開けて入替えたものだと推察できる。こうして僕が勝手に開けたPlayStationは改造されてると推察されて保証対象外になったのです。
 次で実際にプログラムを作ります。

DSA署名をC++で作る(実装)

SSL? 証明書? PKI? それっておいしいの? - Digital Signature Algorithm (DSA) を参考にしています)
役に立つか分かりませんがDSAサンプルコードをVS2010形式のプロジェクトで全部まとめて掲載しました。p,q,gを出すところは無いけどw)
LibTomMathを使って任意精度整数を扱う方法はこちらに記載
DSA署名の概要はこちらに記載

 まずGenerator(署名付けモジュール)を作りますが、さらにその前に鍵を作っておきます。
DSA鍵の元(p, q, g) を決めます。「大きな素数pと素因数qを求め、mod p の世界で q 乗して初めて 1 になる数 g を探します。」何言ってるのか分かりません。でも大丈夫。DSAはJavaに実装されているのがあるので鍵を決めるところまではJavaを使えば出来ます。
public static void main(String[] args)
{
   try {
      // KeyPairGeneratorの初期化
      KeyPairGenerator keygen = KeyPairGenerator.getInstance("DSA");
      SecureRandom random = new SecureRandom();
      keygen.initialize(1024, random);
      // 鍵の生成
      KeyPair keys = keygen.generateKeyPair();
      PublicKey pubkey = keys.getPublic();
      System.out.println(pubkey.toString());
   } catch (Exception ex) {
      ex.printStackTrace();
   }
}
で実行するとDSAの元(p,q,g)とyがダーッと表示されます(実行する度に新しく作られます)。yは公開鍵ですが、対応する秘密鍵xを表示させる方法が分からなかったので秘密鍵xと公開鍵yは作り直します。
p,q,gをディファインしましょう。
#define DSA_P "fd7f53811d75122952df4a9c2eece4e7f611b7523cef4400c31e3f80b65126694\
55d402251fb593d8d58fabfc5f5ba30f6cb9b556cd7813b801d346ff26660b76b9950a5a49f9fe80\
47b1022c24fbba9d7feb7c61bf83b57e7c6a8a6150f04fb83f6d3c51ec3023554135a169132f675f\
3ae2b61d72aeff22203199dd14801c7"
#define DSA_Q "9760508f15230bccb292b982a2eb840bf0581cf5"
#define DSA_G "f7e1a085d69b3ddecbbcab5c36b857b97994afbbfa3aea82f9574c0b3d0782675\
159578ebad4594fe67107108180b449167123e84c281613b7cf09328cc8a6e13c167a8b547c8d28e\
0a3ae1e2bb3a675916ea37f0bfa213562f1fb627a01243bcca4f1bea8519089a883dfe15ae59f069\
28b665e807b552564014c3bfecf492a"
な感じ。折り返さなくても良いです。
秘密鍵は適当に決めて、(p,q,g,x)から公開鍵を作ります。
void Generator::generateKey()
{
   mp_int p, q, g, y, x;
   mp_init(&p);
   mp_init(&q);
   mp_init(&g);
   mp_init(&y);
   mp_init(&x);
 
   mp_read_radix(&p, DSA_P, 16);
   mp_read_radix(&q, DSA_Q, 16);
   mp_read_radix(&g, DSA_G, 16);
   /*秘密鍵・公開鍵を作ります
     x を 0 < x < q  で選んで秘密鍵とする
     y = (g^x) mod p を公開鍵とする*/
 
   //秘密鍵xを選ぶ : qより小さければ良い(自由に決める)
   char x_str[] = "1234567890abcdef1234567890abcdef";
   mp_read_radix(&x, x_str, 16);
 
   //y = (g^x) mod p
   mp_exptmod(&g, &x, &p, &y);
 
   char anschar[1000];
   mp_tohex(&x, anschar);
   std::cout << "#define DSA_X \"" << anschar << "\"" << std::endl;
   mp_tohex(&y, anschar);
   std::cout << "#define DSA_Y \"" << anschar << "\"" << std::endl;
}
 yを求める式でgのx乗が出てきます。これが引っ掛かったところで、累乗はLibTomMathの関数にありません。累乗はさすがに数が大きくなり過ぎるからなんでしょうか。代わりにmodと合わされたmp_exptmod関数があります。関数の説明には 「d = a**b (mod c)」とあります。累乗は「^」で表すと思ってたので探すのに手間取りましたが「y = g**x(mod p)」でぴったりはまります。これを実行すると秘密鍵x、公開鍵yが出力されるのでディファインしておきます(もちろんxはもっとそれらしいランダム値が良いでしょう)。
#define DSA_X "1234567890ABCDEF1234567890ABCDEF"
#define DSA_Y "9C878080D754D4C044D96E96CE68593D6C7F765881BF372609ADECE0216C6C294\
4EC96A418242C212EEE6C2EBC666B81AE21E61F5107F91AF496C7CF508B419EED933A9EA505707AE\
C3740BB6FC27E23A4ABD937AA694EB6F798EDA867B272550D3AA90730039BDFAFC4CB2192D2A2F7C\
2046758E7FEBD8EC61302DFCA8944F1"
さて鍵が全部決まりました。

 ここまでが準備で、ここからが署名付けの処理になります。
 最初に平文からハッシュ値を計算します。平文は数値でないので、計算するにしても平文から数値に変換することが必要です。その数値に対して署名を計算するのです。もちろん平文と数値は1対1でなければなりません。というわけで平文からSHA1のハッシュ値を計算します(平文から数値に変換するのにSHA1を使う、というのもDSA署名の決まりです)。
SHA1はRFC文書のSHA1にC言語のコードが載っていますのでそれをそのまま使います。実際はバイナリ値で出るのでそれを16進数文字列に変換する関数を追加しました。この辺は省略します(実際のコードはこんな感じ、最後にコッソリと関数が追加されてますw)。
   //平文のHash値を求める
   mp_int z;
   mp_init(&z);
   char buf[SHA1HashSize * 2 + 1];
   SHA1GenerateHash(hira_bun.c_str(), buf);
   mp_read_radix(&z, buf, 16);
平文はstringのhira_bunに入っているとします。SHA1GenerateHashでbufに16進文字列でハッシュ値が入るとして、zに代入します。
   //DSAの元や鍵を読込む
   mp_int p, q, g, x;
   mp_init(&p);
   mp_init(&q);
   mp_init(&g);
   mp_init(&x);
 
   mp_read_radix(&p, DSA_P, 16);
   mp_read_radix(&q, DSA_Q, 16);
   mp_read_radix(&g, DSA_G, 16);
   mp_read_radix(&x, DSA_X, 16);
元や鍵を全部読込みます(公開鍵yは使いません)。
   /*r、sを求める 
      r = (g^k mod p) mod q 
      s = k^(-1) (z + x r) mod q 
      (kは0以外のランダムな数字)
      */
   mp_int r, s, k, zero;
   mp_init(&r);
   mp_init(&s);
   mp_init(&k);
   mp_init(&zero);
 
   mp_int t1, t2, t3;
   mp_init(&t1);
   mp_init(&t2);
   mp_init(&t3);
 
   mp_zero(&r);
   mp_zero(&s);
   mp_zero(&zero);
 
   while (mp_cmp(&r, &zero) == 0 || 
          mp_cmp(&s, &zero) == 0) {
 
      //kを選ぶ (ランダム、のつもり・・)
      srand((unsigned)clock());
      int k_int = rand() + 1;
      mp_set_int(&k, k_int);
 
      //r = (g^k mod p) mod q
      mp_exptmod(&g, &k, &p, &t1);
      mp_mod(&t1, &q, &r);
 
      //s = k^(-1) (z + x r) mod q
      mp_mul(&x, &r, &t1);
      mp_add(&z, &t1, &t2);
      mp_invmod(&k, &q, &t3);
      mp_mul(&t2, &t3, &s);
   }
署名r、sを求めるのですが、kというパラメータがいきなり出てきます。 0 < k < q の中でランダムで決めて良いようです。intで適当に決めています(もっと大きな数の方が良いのかもしれません)。Whileループはkの決め方によってrかsが0になってしまう場合があるようなのでその場合はkを選ぶところからやり直すようになっています。大抵は1回で通るのであまり気にしません。
 rを求める式に「(gk mod p)」があります。鍵を求めるのと同様にmp_exptmodを使います。
 sを求める式は「s = k-1 (z + x r) mod q」です。(z+xr)は先に計算するので問題ありません。t3に代入しておきます。問題は次の「 k-1 t3 mod q」です。kのマイナス1乗ですがkで割り算するとうまくいきません。この式に対応する、mp_invmodがあり、関数の説明は「c = 1/a (mod b)」となっています。ぴったり当てはまります。
 どうもこういった計算をプログラムに置き換えるにはexptmodだのinvmodだのの関数を知ってるかどうかが重要で、これさえ知っていれば順番に式を当てはめて行けば出来そうです。

これで署名は全部揃いました。
秘密鍵のxと平文のハッシュ値z以外をstd::mapで返すことにします(簡単なので)。
   //署名を戻り値にセット。
   std::map<char, std::string> map;
 
   char anschar[1000];
   map['p'] = DSA_P;
   map['q'] = DSA_Q;
   map['g'] = DSA_G;
   mp_tohex(&r, anschar);
   map['r'] = anschar;
   mp_tohex(&s, anschar);
   map['s'] = anschar;
   map['y'] = DSA_Y;
 
   return map;
適当なプログラムですがデバッグで見てみるとこうなります。
Generatorの仕事はここまでです。
鍵を作る関数と、平文を与えると署名の入ったmapが返ってくる、という関数だけで良いです。(本当は(p,q,g)も作るようにしたいですが…)

次はVerifier(署名確認モジュール)です。
Verifierはシンプルに署名の入ったmapと平文を与えるとTRUEかFALSE(1か0)が返ってくる関数だけです。まずmapからmp_intに読込みます。
   mp_int p, q, g, y, r, s;
   mp_init(&p);
   mp_init(&q);
   mp_init(&g);
   mp_init(&y);
   mp_init(&r);
   mp_init(&s);
 
   mp_read_radix(&p, map['p'].c_str(), 16);
   mp_read_radix(&q, map['q'].c_str(), 16);
   mp_read_radix(&g, map['g'].c_str(), 16);
   mp_read_radix(&y, map['y'].c_str(), 16);
   mp_read_radix(&r, map['r'].c_str(), 16);
   mp_read_radix(&s, map['s'].c_str(), 16);
平文からSHA1のハッシュ値を求めます。
   //平文のHash値を求める
   mp_int z;
   mp_init(&z);
   char buf[SHA1HashSize * 2 + 1];
   SHA1GenerateHash(hira_bun.c_str(), buf);
   mp_read_radix(&z, buf, 16);
当然全く同じコードです。で、署名の正当性チェックですが、「((gz yr)(s-1) mod p) mod q = r」が成立するかどうか、だそうです。累乗が沢山あるしこれはさすがに厄介です。
そこで作ったのが以下のコードです。
   mp_int w, v;
   mp_init(&w);
   mp_init(&v);
 
   mp_invmod(&s, &q, &w); // generate W
   generateV(&v, &y, &p, &q, &g, &w, &r, &z);
 
   if (mp_cmp(&r, &v) == 0) {
      return TRUE;
   } else {
      return FALSE;
   }
中のgenerateV関数は↓です。
void Verifier::generateV(mp_int* v, mp_int* y, mp_int* p, mp_int* q,
                         mp_int* g, mp_int* w, mp_int* r, mp_int* z)
{
   mp_int  u1, u2, u3, u4, t1, t2, t3, t4;
   mp_init(&u1);
   mp_init(&u2);
   mp_init(&u3);
   mp_init(&u4);
   mp_init(&t1);
   mp_init(&t2);
   mp_init(&t3);
   mp_init(&t4);
 
   mp_mul(z, w, &u1);
   mp_mod(&u1, q, &u2);   // remainderの代わり
   mp_mul(r, w, &u3);
   mp_mod(&u3, q, &u4);   // remainderの代わり
 
   mp_exptmod(g, &u2, p, &t1);
   mp_exptmod(y, &u4, p, &t2);
 
   mp_mul(&t1, &t2, &t3);
   mp_mod(&t3, p, &t4);   // remainderの代わり
   mp_mod(&t4, q, v);     // remainderの代わり
 
   mp_clear(&u1);
   mp_clear(&u2);
   mp_clear(&u3);
   mp_clear(&u4);
   mp_clear(&t1);
   mp_clear(&t2);
   mp_clear(&t3);
   mp_clear(&t4);
}
Q:これは何?さっきの式がどうしてこうなるの?
A:どうしてそういうこと聞くの?(´・ω・`)

 というわけでgenerateVを行うとvに先ほどの式の結果が代入されますのでrと比較し、同値ならTRUE、違うならFALSEを返します。
 上記コードはJavaのDSA署名のチェックコードをそのままC++に置き換えたのでイマイチ説明できません(generateWとgenerateVに分かれれるのもそのせいです)。もちろん、掛け算、mod演算、invmod演算、exptmod演算だけでやってるので式をよーく変形すれば出来るのでしょう(近くの数学の得意そうな人に聞けばきっと喜んで説明してくれると思います)。コード中でremainderの代わり、と書いてあるのはJavaではreminder関数を使っていますが、LibTomMathには無かったのでmodで代用しました、と言うメモです。要は割り算の余りが分かれば良いらしいので問題無いでしょう。
 動かしてみれば分かりますがこれで立派に平文と署名の正当性チェックは出来ます。ズルしようが何しようが動くコードを入手するのが最優先なのです。コードパクるのは最後の手段だけどね。

 サンプルコードのプロジェクトはSAMPLE/SAMPLE.cppがメインです。出力とかもしないのでDebug実行して値を確認する用です。
 署名はサンプルコード内では16進文字列でやりとりしていますが、実際はDSA暗号であることも隠すため、ちょこっと暗号化して付加します。数は全部2倍しておくとか、決まった数を足しておくとか、それをZip圧縮してBase64でエンコードするとか、GeneratorとVerifierで示し合わせて簡単には署名や元が取り出せないような暗号化です。DSAと組み合わさると強力です。
 また、サンプルはmapに署名と鍵全部の情報を入れましたが署名はrとsだけで、(p,q,g,y)はどのファイルでも変わらないのでVerifierに埋め込んじゃっても良いと思います。
 もちろんGeneratorは切り離して作り、間違って外に出さないことが必要です。
 あとVerifierを分かり易い名前で外部DLLにすると、せっかく頑張ったのに必ずTRUEを返す偽DLLに差し替えられるとかのハック手段があるかも知れません。正しい認証のためには正しい公開鍵を使って正しいモジュールで計算して認証する、という前提が必要です。

2013/12/18

DSA!DSA!

私の声があなたに届かなくとも。あなたの声は私に届いている!世界中に届いている!ビルを壊したやつらにもすぐに届くだろう(Wooo!!USA!USA!USA!)」と、かつてブッシュ大統領は瓦礫の上で言いました。まさにインディペンデンスデイみたいでした(youtube)。相手が宇宙人でなく同じ人間同士なのがなんとも…。

 さてUSAではなくDSAですが、せっかく任意制度整数LibTomMathを紹介したのでそれを使ってDSA電子署名の計算をするサンプルコードを紹介したいと思います(あとで)。なぜかというと、DSAは学問的な解説は多くあるものの、実際にその計算をコードで解説してるようなのが見つからず、Javaソースを読んだりして自力で出来たのが嬉しかったので。
 論より証拠、とよく言いますが、この世界では「理論よりサンプルコード」「この計算は何をやってるんだろう… いや細けぇこたぁいいんだよ!動けば!」的な感じが多いです。なにぶん時間が無い(休憩時間が少なくなるという意味)もんで。(そして最も嫌なタイミングで障害が発覚しますよね…)。

 作りました。DSA署名するサンプルコード!

2013/12/17

レンジでチン!

「ここ〇〇ホテル(某高級ホテル)でしょ?」と、ある客は言いました。
何年も前ですが、母とそのホテルのロビーの喫茶店で昼食をしてたときのこと(何でそんなとこに行ったのかは覚えてない)、客は僕らともう一組の親子(母と子共)しか居なくて静かだったので何が起こったのかよく覚えてます。
 その親子は最初に何か文句を付けてサンドイッチを引込めさせて、作り直して出てきたサンドイッチがまだ不満だったらしく「ホットサンドなのに冷たいじゃないのコレ。そんなんで良いの?ここ〇〇ホテルでしょう?」とかなりきつい口調で主張したのだ。子供はまだ小学生くらいだったと思う。制服を着ていて良い学校に行ってるんだろうなという雰囲気だった。しかし母親があんなヒステリックに従業員を怒鳴ってたら嫌だろうなぁ、とか、〇〇ホテルだっけ?△△って名前じゃなかったっけここ、などと思って見てた(日本名と外国名2つ名前があるようだ)。
 それだけだとちょっとやな感じのお金持ちの人という印象だったが、今度はオムライスを注文した僕の母が一言、「オムライスの中身が冷たい」。
  恐らく厨房の電子レンジの調子が悪かったんでしょう。僕のビーフカレーだけは良く温まっていてとても美味しかったです。しかし冷凍食品かレトルト食品を温めるだけで1500円も取るんだから電子レンジくらい良いのを置いておいてくださいな…。

 母はそのオムライスの温まってる部分だけを食べて出ていくことにしたようだ。でもそこはちゃんと主張した方が良かったよなぁと今も思う。
 しかし「そんなんで良いの?ここ〇〇ホテルでしょう?」っていうフレーズが強烈過ぎてすっかりタイミングを逃してしまったのだ。それともホテル側に「あの客には冷えたオムライスでいい」と思われたか?

刑事のまなざし

刑事のまなざし」見ました。
すっごい面白かった。
そもそも普段ドラマなんて見ないのになぜ見たかというと、「あら井之頭ゴローさん(孤独のグルメの)が出てる」と思ったんで。
そういえば大学とかサボってるとき「はぐれ刑事純情派」の再放送よく見てたし、刑事ドラマの人情ものはかなり好きらしい。
DVDのレンタルが出るか分からんが何か機会があればゆっくり全話見直してみたい。
出てる俳優さんが皆良かったと思う。椎名桔平は文句無くカッコいいし。
少年(?)犯罪の犯人すごい雰囲気があってたし、ゴローさん(じゃないけど)も面白いしカッコいいし、椎名桔平を見張ってる偉そうな人も意地悪に見えてかなり人情派でそれがまたいい。女刑事の人もちっこくて良かったし。
終わってから言ってもあれだけどかなーりお勧めなドラマ。
そして来週からは安顕が出るドラマだ。見ないとw

北のカナリアたち」を少し見ました。
さして興味が無かったので10時までNHKスペシャルの醤油作りにおける麹の凄さみたいな番組を見ていました(これ)。これも最初からは見れなかったんだけどただのドキュメンタリーでなくて醤油の製造工程とか和食の仕込みとかが綺麗な映像で撮れていてすごく良かった。NHKスペシャルはたま~~に良いドキュメンタリーを作りますね。
さてそれが終わった後、北のカナリアたちを途中から見始めましたが全く共感出来る部分が無くて見るのが苦痛だったので40分くらいで止めました。あれは吉永小百合の大ファンじゃないとキツイと思う。

2013/12/13

STLのmapはスレッドセーフでなかった

std::mapを勝手にスレッドセーフだと思い込んで並列アクセスしてたらたまに落ちる、という障害になってしまった。
std::mapは書込みの際は必ずMutexで保護しましょう。
その他標準テンプレートライブラリを使う際はスレッドセーフかどうか調べてから使いましょう。自戒。

2013/12/09

C/C++で天文学的数字を扱う - LibTomMathの紹介

Java製の暗号化、復号化を行うライブラリをC/C++に移植したことがあるが、その時にJavaのBigIntegerに当たるものがC/C++に無くて困った。
BigIntegerは300桁くらいの数値でも平気で掛け算したり出来るライブラリだ。twitterが普及した時に140字って意外と長いと思ったが、ツイート2つ分くらい、ダーッと数字が並んでるのを想像すると良い。こんな数字は天文学か暗号でしか使わないはずだ…。

さてC/C++には標準でそういったライブラリは含まれていない。で、Boostとかライブラリはいろいろあるんだが有償だったりライセンスが複雑だったり、使い方もそれぞれ全然違うし、何を使えば良いんだ?と困った。 またこういったライブラリでも出来る演算が限られていたりする(忘れたが例えばmod演算の関数が無い、など)ので、必要な関数が全て揃っているか注意すること。

でいくつか探した中でこれが最高と思ったのがLibTomMathというライブラリ。
LibTomというプロジェクトの一つで、Tomさんが作ってるんでしょうか。
他のライブラリは巨大なライブラリ群の中に数値ライブラリが含まれる、というのが多くていかにも重くなりそう(プロジェクトが) だったが、これは数値ライブラリだけの小型のものだった。
また素晴らしいのはライセンスがPublic domainで、それを示す文言もパンクだ。
LibTomMath is public domain.  As should all quality software be.
https://github.com/libtom/libtommath/blob/master/LICENSE
Boostも商用アプリに組み込めるライセンスらしい(どうも文章が固くてだめなのかなと思ってしまった)。C/C++用の任意制度整数ライブラリとしてはLibTomMathはマイナーな方かもしれない。小さくて良いんだけどなぁ。

せっかくなので布教活動として、VisualStudio2010でLibTomMathを使う方法のチュートリアルを作成する。(LibTomMathを使ってDSA署名とそのチェックを行う方法もこちらに

まずLibTomMathのGitHub(https://github.com/libtom/libtommath)より、「Download ZIP」でプロジェクトフォルダをダウンロードしておく。

VS2010でC++プロジェクトを作る(SAMPLEというWin32コンソールアプリを作った)。
出来たフォルダの中にダウンロードした「libtommath」のプロジェクトフォルダ(解凍して出来たフォルダ丸ごと)をコピーする。
 VS2010のソリューションビューのソリューションを右クリック「追加」→「既存のプロジェクト」を選ぶ。
 「libtommath.dsp」を選ぶ(vcprojではない)。追加の過程でVS2010形式に変換される。
SAMPLEプロジェクトのプロパティを編集する。
共通プロパティ、Frameworkと参照の「新しい参照の追加」で「libtommath」を追加する。
構成プロパティ、C++の全般の「追加のインクルードディレクトリ」にlibtommathのディレクトリを追加する。SAMPLEからの相対パスで良いのでこんな感じ。
SAMPLEプロジェクトでlibtommathが使えるようになる。
文字列で現された巨大な数を2つ読込み、掛け算した結果を文字列で表示するサンプル。
#include "stdafx.h"
/*
 * license
 * LibTomMath is public domain. As should all quality software be.
 * Tom St Denis
 */
#include "tommath.h"
 
int _tmain(int argc, _TCHAR* argv[])
{
 mp_int a;
 mp_int b;
 mp_int answer;
 mp_init(&a);
 mp_init(&b);
 mp_init(&answer);
 
 mp_read_radix(&a, "10000000000000000000000000001", 16);
 mp_read_radix(&b, "1234567890ABCDEF", 16);
 
 mp_mul(&a, &b, &answer);
 
 char anschar[1000];
 mp_tohex(&answer, anschar);
 printf(anschar);
 return 0;
}
mp_intの変数宣言の後、mp_initで初期化が必要、ということ以外はかなり直感的な使い方が出来ることが分かる。簡単。Boost使うのに比べたらすごい簡単と思う。
mp_clear(&a);
mp_clear(&b);
mp_clear(&answer); 
mp_clear忘れてました。deleteとかfreeみたいなもんです。忘れないようにw

USB3.0のポートに刺すと抜けなくなった

何の気なしにUSBメモリをプスッと刺してしばし操作。
さぁ抜いて帰ろうと思ったら抜けない。
引っ掛かったかなと思ってちょっと強く引いても抜けない。
グニグニ動かしても抜けない。何か引っかかってる。
これ以上はポートかコネクタか、どっちかが限界を迎えて壊れるだろうなと思うくらい強く引いても抜けない。
ついに「USB3.0 抜けない」で検索をかけたりした。
で、上方向(コネクタのUSBマークの付いてる側)にちょっと押しながら引くとスッと抜けた。

危ない、壊すところだった。
しかしUSB3.0ポートで抜けなくなるという情報がほとんど無いことを考えると、この返し(?)のついたポートはDELLの独自仕様かな?

これUSB3.0じゃなくてe-SATAとUSBの兼用ポートだった。
e-SATAのポートは抜けにくく作られているらしい。
でそれがUSBのコネクタにも引っ掛かるということだが、どっちにしろUSBだと思って刺して抜けなくなると軽くパニクるww

2013/12/06

レジストリの値から言語を判定する

Windowsのレジストリの値を見てシステムロケールの判定を行う方法

「HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Nls\Locale」
の(Default)が0411ならOSの言語設定が日本語である。
InstallShieldでインストール要件に「日本語の時だけ」という前提条件を付けたかったので調べた。

実は
「HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Nls\Language」
のDefaultが「0411」なら、という条件にしてしまった。こちらはインストーラをどの言語で動かすか、のデフォルト値だ。
ほんとはOSのLocaleに合わせるべきだったような気がする。まぁ英語OSを使う人がインストーラは日本語で動かしたい、なんてことも無いだろうから気にしない。

最近の記事を読み返すと押し通すだの気にしないだの、ほんと何ていうか、いい加減なエンジニアだなぁと思う。 インストーラってアプリ本体とは切り離されたものだからちょっといい加減になっちゃうんだねきっと。
その割にちょっと間違うとインストール自体出来ないという最悪の事態に陥り、すぐ直せ&全社掲示てことになる。
「0411」は日本語だが他の言語がどの数字か分からなかったが、恐らくこの辺の情報で良さそうだ。

2013/12/03

_IsMaintenance = "Reinstall"

前回の、「常にオーバーライドする」をチェックだけでは再インストールの時にやっぱりうまくいかないことが分かった。

今担当しているインストーラは再インストールの際に「修復」や「削除」などを選ばせない、シンプルな作りのものだ。
一般的なインストーラの再インストールではメンテナンスモード選択ダイアログに進むが、それを単純にスキップしている。
ただしそれだと再インストールしているように見えて実は何もしていないということが起こるのだ(ファイルが壊れていても直してくれないので困ってしまう)。
メンテナンスモード選択のダイアログを処理することで再インストールするモードに設定されるのだが、ダイアログと一緒にそれもスキップされている。

再インストールでメンテナンスモード選択のダイアログが不要な場合、「再インストールするモードに設定」という処理を他のダイアログに追加すると良い。
今回は最初の「ようこそ」の次、必ず通るダイアログに設定した。
そのダイアログの「Next」ボタンのイベントに「Reinstall」を追加、引数は「ALL」、条件は「Installed」とした。「製品が既にインストールされている場合、Reinstall(ALL)とする」というくらいの意味だろう。また、イベント「ReinstallMode」も同様に条件「Installed」で追加すると良いだろう。「ReinstallMode」の引数についてはMSDNが本家だ。デフォルトは「omus」で変更のあったファイルだけ更新、他には「vamus」とか「amus」を指定するのが一般的のようだ。まぁオプションをよく読んで必要な指定をすること、今回はデフォルト「omus」でいいや。

ちなみにこれも正解にたどり着くのに随分時間がかかった。いくつかのサイトにはプロパティ「_IsMaintenance」に「Reinstall」を設定せよ、とあるがこれは単にメンテナンスモード選択ダイアログのデフォルトが修復(Reinstall)になるだけだ。メンテナンスモード選択ダイアログをスキップする場合、そのダイアログの「Next」ボタンのイベントを他のダイアログでやらねばならん。こういうシンプルなインストーラは少数派なんだろうか。
InstallScript実行中にメッセージダイアログを表示させるサンプルコードを載せておく。
MessageBox ("Some message.", INFORMATION);
2つ目の引数はデバッグならINFORMATIONが良いと思うが別にWARNINGでもSEVEREでも良い。文字列しかダメだと思うので数値は文字列に変換して表示させる(NumToStr (dst_str, number)で)。

MSIRESTARTMANAGERCONTROLというプロパティについて。
再インストール時にそのアプリが起動したままだとFileInUseダイアログが出て「起動中の物を閉じてください」とメッセージが出る。これはデフォルトの動作であまりカスタマイズが効かない。まぁ便利だな、くらいに思ってたのだが、当環境で「tomcat」(全然関係無いのに)だの「エクスプローラー」だのを閉じなさいと言われてしまった。中には「Windows® インストーラー」もあった。それはお前だろうと。
何かインストール対象としてチェックするファイルが間違っているような気がしてならないが、どこを見れば良いのかさっぱりだ。探すと「エクスプローラー」を閉じろと言われて困っている人は他にもいたようで、「MSIRESTARTMANAGERCONTROL」というプロパティに「Disable」を設定すれば良いらしい。ちょっと英語力の関係で意味が分かっていないが時間が無いので今回は問題が無ければこれで押し通す。