パスワードハッシングのcrypt関数の使い方
Unix の crypt() 関数とは、パスワードハッシングを実行する関数です。
読み方
- crypt
- くりぷと
目次
概要
FreeBSD では、crypt()関数は、 libcrypt で提供されます。 crypt() は、 pam_unix や pw で使われています。
crypt()関数の役割
crypt()関数の役割は、パスワードの安全なハッシュ値を求めることです。 ユーザのパスワードを設定するときに crypt 関数でハッシュ値にして、/etc/passwd といったユーザのアカウント情報を格納するファイルに、ハッシュ値を格納します。
ユーザがログインなど、パスワード認証をする時に、ユーザからパスワードの入力を受け、 /etc/passwd などからパスワードのハッシュ値を読み取り、パスワードをcrypt()でハッシュ値にし、/etc/passwd 等から得たハッシュ値と一致するかを調べます。 全く同じ値になれば、本人確認は成功したことになります。同じ値にならなければ、認証失敗になります。
パスワードを登録する場合は、 crypt (ユーザの入力したパスワード, システムで生成した乱数のsalt) で計算します。 パスワードが登録されているユーザを認証する場合には、 crypt(ユーザの入力したパスワード, システム内に登録されているパスワードハッシュの文字列) を計算し、システム内に登録されているパスワードハッシュの文字列を一致するかを確認します。なぜ、cryptに「システム内に登録されているパスワードハッシュの文字列」を渡すかというと、その中に「システムで生成した乱数のsalt」が含まれているからです。同じパスワードハッシュを計算するためには、同じ値の salt でなければ、同じハッシュは生成されません。
扱えるアルゴリズム
アルゴリズムは、時代に応じて変わりますが、以下のアルゴリズムは利用できます。
- MD5
- Blowfish
- NT‐Hash
- (unused)
- SHA‐256
- SHA‐512
crypt関数の動き
crypt関数は、設定されたハッシュ関数に対応する関数を呼び出します。 ここでは FreeBSD の libcrypt の SHA-512 のパスワードハッシュを求める場合を例として扱います。
crypt は /usr/src/lib/libcrypt/crypt.c に実装されています。 SHA-512 で crypt する場合には、 /usr/src/lib/libcrypt/crypt-sha512.c の関数が呼び出されます。 crypt から crypt_sha512() が呼び出され、さらに crypt_sha512_r() が呼び出されます。
crypt_sha512_r() では、以下のようなことが行われます。
- 先頭に $6$ があるかどうかチェックし、存在すれば、その分を進めたポインタを salt に格納します。
- salt に rounds= のプレフィックスがあるか確認します。rounds=があれば、ラウンドの値を srounds に取り出します。salt のポインタを rounds=値$ 分を進めます。
- salt の長さを計算します。
- key の長さを計算します。
- SHA512_Init() で初期化します。
- SHA512_Update に key を与えます。
- SHA512_Update に salt を与えます。
- SHA512_Update に key を与えます。
- SHA512_Final で結果を取り出します(alt_result)。
- key が 64 をより長ければ、 SHA512_Update に alt_result を与え、 key から 64 を引き、さらに 64より大きければ、同じ処理を繰り返します。
- SHA-512_Update に alt_result を与えます。
- key の長さに応じて、さらに SHA512_Updateに alt_result を64文字与えたり、 key を与えます。
- SHA_Final で結果を取り出します(alt_result)。これは、中間結果です。
- SHA512_Init() で初期化します。
- key の長さの回数まで、 SHA512_Update に key を繰り返し、与えます。
- SHA512_Final で ダイジェストを取り出します(temp_result)。
- バイトシーケンス P を作成します。 memcpy で、 temp_result をコピーします。key の長さで 64の単位でコピーされます。
- バイトシーケンス S を作成します。P と同じ方法で作成されます。
- CPU サイクルを焼き尽くすために、SHA512を使って、実行を繰り返します。
- rounds で定義されている回数だけ、繰り返します。
- カウンタの値によって、処理が変わります。 P や S, alt_result などを SHA512_Update に与えます。ループごとに SHA512_Init が呼ばれ、 SHA512_Final で中間結果を取り出します。
- パスワードフォーマットを整形します。
- b64_from_24bitを使って、alt_resultをbase64します。
- 最後に buffer のポインタを返します。
pwコマンドから使う場合
pw コマンドから crypt 関数を使う場合には、 pw_user.c で 32文字の salt が乱数によって生成されます。 arc4random_uniform() と呼ばれる関数を使って、ランダムの値を生成し、その値から salt に使える文字列の添字として、文字を取り出して、salt の文字列を作成します。
ヘッダファイル
FreeBSD では、以下の関数が提供されます。
#include <unistd.h> char * crypt(const char *key, const char *salt); const char * crypt_get_format(void); int crypt_set_format(const char *string);
ソースコード
ここでは SHA-512を使用して、パスワードをハッシュ値にします。 SHA-512だとsaltは16文字なので、32文字のsaltを設定していますが、自動的に16文字に切り詰められます。
/* * crypt.c * Copyright (C) 2017 kaoru <kaoru@localhost> * * Distributed under terms of the MIT license. */ #include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <string.h> int main(int argc, char *argv[]) { char *key = "hoge"; char *salt = "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"; char *p; crypt_set_format("sha512"); p = crypt(key, salt); if (NULL == p) { perror ("can not crypt"); exit (1); } printf("%s\n", p); char *q = crypt(key, p); if (NULL == q) { perror ("can not crypt"); exit (1); } printf("%s\n", q); printf ("%d\n", strcmp(p, q)); return 0; }
コンパイル
cc -lcrypt crypt.c
実行例
./a.out
関連項目
ツイート