パスワードジェネレーターpwgenをJavaScriptにしてブラウザで動かしてみた

Linuxではpwgenという読みやすいパスワードを生成してくれるコマンドがあって、Ubuntuなら`sudo apt install pwgen`でインストールすることができます。実行すると以下のような感じでパスワードを生成してくれるだけでなく、あいまいな文字を含めないとか、特殊文字を含める含めない、大文字を含める含めない等々、オプションも豊富です。

```console:pwgenの実行結果
$ pwgen 
Moo4hief xie7OuGh Eil4boo4 Eethahf1 aig9ahPh ooPh3ahg uch1uRis ieHah6sh
aefoh6uZ QuekuF5E uVeig9Ee Miosaht1 yu2Ios4r Ij0aHoox jue4Ha0t Jae0mahj
Umierah9 De5ieci0 eeShee9v thah4Jee gae8Iech Shaecov5 aSh5ohNg lu8zaiG1
oiNg7eeD faiGie9r eeh3Quu4 eeTeiw8Z Eit8PuH0 Or0tei8r Ael5poob yoh4zeiR
…（以下省略）
```

このコマンドを発見した時はなかなか便利そうで面白いコマンドだなー思ったものの、それで終わってしまっていたのですが、最近ふと、Linux以外の環境でも、いつでもどこでも使えたらいいかもなー、画面いっぱいのパスワードを眺めていられたら幸せかもなーって思ったので、じゃあということでJavaScriptにしてブラウザで動かしてみることとしました。

## ソースコードは？ライセンスは？
ソースコードは Ubuntuなら `apt source pwgen` でゲットできます。もし「E: You must put some 'source' URIs in your sources.list」 と怒られてしまう場合は、 `software-properties-gtk` を実行して source code のところにチェックを入れればできるようになります。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/128670/28ce24fb-3baf-eeb4-ff85-10520029cd58.png)

ライセンスはソースコードに書いてあることが多いので、まずはゲットしたソースコードを見てみます。しかしLICENSEファイルもなければ、ソースコード中のコメントは「This file may be distributed under the terms of the GNU Public License」とのこと。GPLの元で配布されることがあるという意味でしょうか？？？英語力のない私には理解に苦しむというかすっきりしない内容となっています。

すっきりしないので、次の手段で /usr/share/doc/ を眺めてみます。すると /usr/share/doc/pwgen/ に copyright ファイルを発見することができました。そのファイルを見るとどうやら GPL-2 のようです。

```text:/usr/share/doc/pwgen/copyright
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: pwgen
Upstream-Contact: tytso@mit.edu
Source: http://sourceforge.net/projects/pwgen/files/pwgen/

Files: *
Copyright: 2001, 2002, 2005, 2006, 2014 Theodore Ts'o <tytso@mit.edu>
License: GPL-2
 This program is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License, v2, as
 published by the Free Software Foundation
 .
 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.
 .
 You should have received a copy of the GNU General Public License along
 with this program; if not, write to the Free Software Foundation, Inc.,
 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 .
 On Debian systems, the complete text of the GNU General Public
 License version 2 can be found in `/usr/share/common-licenses/GPL-2'.
```
ちなみに私のPCにインストールされているpwgenは2.07で、ゲットできたソースコードも2.07でした。今回はそれをベースとすることとしました。

## 実際に動くのはこれ
実際に動くJavaScript版のpwgenは https://hachisukansw.github.io/pwgen/ で試すことができます。スマホで動かすことは考えずに作っていたのですが、試してみたら動くようなので申し訳ない程度に METAタグのviewport を追加しました。

## オプションの指定方法
オリジナルはコマンドラインなので`pwgen [オプション値]`という形式でオプションを指定できましたが、このブラウザ版ではURLの ? 以降に指定をします。URLで複数指定する場合は & でつなぎます。`/pwgen?-A&-B`という感じです。 -N や -H のように値が必要な場合は`/pwgen?-N=100`のように指定します。

## オリジナルからの変更点

### sha1.c
SHA-1のハッシュを計算するプログラムです。Cのマクロに慣れていない等々の理由でこれをJavaScriptに変換することは断念して別のSHA-1を求めるプログラムと差し替えて実現しています。差し替えたソース元はRFC 3174のドキュメント（ https://www.ietf.org/rfc/rfc3174.txt ）中に記載されていたCのプログラムです。それを選んだ理由は、ぱっと見た感じでJavaScriptに変換しやすそうに思えたのとテストプログラムもついていたからです。

### randnum.c
乱数を発生させるプログラムです。 /dev/urandom（又は/dev/random）を使用するようになっていたので、それは無理かなっとさららっと変換することは断念して、 window.crypto.getRandomValues が使える場合は使って、使えない場合は Math.random を使用するように書きなおしています。

### pwgen.c
中身はmain()関数とヘルプ文字列なので、ウェブで使うために全面的に書きなおしています。

### -Hオプション
オリジナルでは -H で指定したファイルのハッシュ値を計算するようになっていましたが同じことが難しかったので -H で指定した文字列のハッシュを計算する仕様に変更しています。ハッシュ元がまるわかりですし、ブラウザの履歴に保存されちゃう可能性もあるので使う際は注意が必要かなって思います。オリジナルでもパス名は .bash_history を見れば分かっちゃったりしましたが、それ以上に注意はいるんじゃないかなと思っています。

`-H=abc`と以下のコマンドは同じ結果となります。

```console:-H=abcと同じ結果
$ /bin/echo -n "abc" > file.txt
$ pwgen -H file.txt
```

またオリジナルではシード値を # 以降で指定できましたが#はURL中で # はうまく扱えないため : で指定できるようになっています。`-H=abc:def`と以下のコマンドは同じ結果となります。

```console:-H=abc&#58;defと同じ結果
$ /bin/echo -n "abc" > file.txt
$ pwgen -H file.txt#def
```

### -hオプション
オリジナルは英語でしたが日本語にしています。といってもgoogle翻訳で翻訳してちょこっと変更しただけのものですが、、、。以下はその内容です。

```text:-hオプション（日本語）
Usage: pwgen [ OPTIONS ] [ pw_length ] [ num_pw ]

pwgenでサポートされるオプション:
  -c or --capitalize
	パスワードに少なくとも1つの大文字を含める（デフォルト）
  -A or --no-capitalize
	パスワードに大文字を含めない
  -n or --numerals
	パスワードに少なくとも1つの数字を含める（デフォルト）
  -0 or --no-numerals
	パスワードに数字を含めない
  -y or --symbols
	少なくとも1つの特別な記号をパスワードに含める
  -s or --secure
	完全にランダムなパスワードを生成する
  -B or --ambiguous
	あいまいな文字をパスワードに含めない
  -BB or --ex-ambiguous
	あいまいな文字をパスワードに含めない（過剰版）
  -h or --help
	ヘルプメッセージを表示する
  -H or --sha1=text[:seed]
	与えられたテキストのsha1ハッシュを（そうではない）ランダムジェネレータとして使用する
  -C
	生成されたパスワードを列表示する（デフォルト）
  -1
	生成されたパスワードを列表示しない
  -v or --no-vowels
	母音を使用しない

フィルター:
   --uniq
	同じパスワードを含めない
   --grep-upper
	大文字を含むものに限定する
   --grep-lower
	小文字を含むものに限定する
   --grep-digit
	数字を含むものに限定する
   --grep-punct
	特殊文字を含むものに限定する
   --uniq-char
	パスワード中に同じ文字を含めない
   --same2char
	パスワード中に同じ文字を2つ以上含める
   --same3char
	パスワード中に同じ文字を3つ以上含める
   --reading
	パスワードの読みを追加する
```

### -BBオプション
-B（あいまいな文字除外）の過剰版です。

```text:pw_rand.c
（オリジナル版 -Bオプション）
const pw_ambiguous = "B8G6I1l0OQDS5Z2";

（過剰版 -BBオプション）
const pw_ambiguous = "B8G6bI1l7i0OoQDSs5Zz29qgadCcKkUuVvXx";
```

### フィルター
オプションで -c を指定しても大文字が含まれないことが稀にあって、困ることもありそうなので、結果から大文字を含むものだけに限定する`grep[A-Z]`等のフィルターを追加してみました。