タッチタイピングの練習を兼ねた“いい感じ”のパスワード生成ロジック

2011 年 1 月 17 日 by 山平

タッチタイピング、できますか?

いつまで経っても習得できない私は、パスワードで練習しています。

  1. 定期的にパスワードを変更する
  2. パスワード生成ツールで複数候補を作成する
  3. 候補の中で一番キーが散っているパスワードを採用する
  4. 繰り返し…

毎日パスワードを変更するわけではないので、上の手順を面倒と感じるほどでもないのですが、「意図的にいい感じの散り具合を実現する」にはどうした良いのだろうか?と思い実験してみたので記録しておきます。

キーのマッピング

散り具合を判断するためにはキーの配置が数値化されていないと話になりません。
ということで、ちょっと単純化して以下のように作成します。

keys = ""+
"1!  2\"  3#  4$  5%  6&  7'  8(  9)  0   -=  ^~  \\|" +
"  qQ  wW  eE  rR  tT  yY  uU  iI  oO  pP  @`  [{  " +
"  aA  sS  dD  fF  gG  hH  jJ  kK  lL  ;+  :*  ]}  " +
"    zZ  xX  cC  vV  bB  nN  mM  ,<  .>  /?  \\_    "
keys.split("\n").each do |line|
#行ごとの処理
line.scan(/../).each do |key|
#列(半角2文字)ずつ処理
end
end

半角2文字ずつ取り出して処理する際の書き方を思いつくまで苦労したのでメモしておきますが、「String#scan(/../).each」が一番シンプルかと思います。

String#scan

パスワードの生成

意図的に散らせるのが目的ですが、ランダムな要素がないと常に四隅に配置された状態を最も「散っている」と判断してしまうと考え、方針を変更しました。

  1. 使用するキャラクタをランダムに取り出して指定した長さのパスワード候補を作成する
  2. 生成するパスワード候補は指定数*2(ただし最低4)
  3. 評価基準に沿って候補をソートし、上から指定数を取り出して結果とする

パスワードの評価

パスワードをランダムに生成する方針に変えたため、この評価の部分に最も注力しました。
生成したパスワード候補から3つの指標を算出し、それに基づいて候補をソートしています。

1.隣り合う文字列の散り具合

パスワード文字列の隣り合うキー同士の距離を算出し、パスワード候補の標準偏差を算出しています。
つまり、同じ文字の組み合わせでも、並びによって評価に差がでます。

例)QWERTY < QYWTER

2.パスワード文字列全体での散り具合

パスワード文字列に出現するすべてのキーについて、他のキーとの距離をすべての組み合わせで算出し、パスワード候補の標準偏差を算出しています。
つまり、並びに関係なく重複するキーや隣接するキーの有無によって評価に差がでます。

例)AGAGAG < QZECTB

なお、この評価については上で述べたように四隅に寄っていた方が評価が高くなりがちですので、四隅のキー(1Z\_)を加えた状態で評価しています。

3.出現文字種の散り具合

パスワードに出現する文字の種別(数字、英大文字、英小文字、記号)の出現数をそれぞれカウントし、パスワード候補の標準偏差を算出しています。
つまり、キーの座標が同じでも、文字種の複雑さによって評価に差がでます。

例)12qw3e < !2Qw#e

ただし、パスワードに使用できる文字種を指定できるようにしたため、この指標がまったく意味をなさない場合もあります。

パスワード候補のソート

上で算出した評価点に基づいてパスワードをソートし、上から生成数ぶん取り出してパスワードの生成結果とします。
ただし、SQLのORDER BY区のようにソート順をきっちり決めることはできません。
パスワードの散り具合の評価として、出現文字種の複雑さが必ず最優先されるというのは考えにくいのです。

そこで、各評価点に重み付けを行い、合計点の高い順にパスワードとしての評価が高いと判断することにしました。
Rubyの場合、いわゆるMix-Inというヤツを使えば大小関係の比較まわりの実装が済んでしまうのでとても楽です。

def &lt;=&gt;(other)
(other.dist_neig &lt;=&gt; self.dist_neig) * WEIGHIT_DIST_NEIG +
(other.dist_all &lt;=&gt; self.dist_all) * WEIGHIT_DIST_ALL +
(other.complex &lt;=&gt; self.complex) * WEIGHIT_COMPLEX
end

Comparableモジュールを組み込んで<=>メソッドを定義すれば完了です。

Comparable

パスワード評価の確認

ここまでの実装でパスワードを生成してみました。
条件は

  • 文字数:8桁
  • 文字種:半角英数
  • 生成数:4

結果は以下のようになりました。

l1ukT2tp
fo0218Dj
PR62Q1Bl
fgVd1h12

思った通りにうまくパスワードが生成された…のでしょうか?

さて、どうやって評価しましょう?
この結果を見ながらソートのウェイトを調整していこうと考えたのですが。。。
生成された文字列の散り具合を評価するロジックが必要なことに気がついてしまいましたが、パスワードの生成に気力を使い果たしてしまったので今回はここまでにします。

以上です。

タグ:

TrackBack