パスワードハッシングのcrypt関数の使い方

提供: C言語入門
移動: 案内検索
スポンサーリンク

Unixcrypt() 関数とは、パスワードハッシングを実行する関数です。

読み方

crypt
くりぷと

概要

FreeBSD では、crypt()関数は、 libcrypt で提供されます。 crypt() は、 pam_unix や pw で使われています。

crypt()関数の役割

crypt()関数の役割は、パスワードの安全なハッシュ値を求めることです。 ユーザのパスワードを設定するときに crypt 関数でハッシュ値にして、/etc/passwd といったユーザのアカウント情報を格納するファイルに、ハッシュ値を格納します。

ユーザがログインなど、パスワード認証をする時に、ユーザからパスワードの入力を受け、 /etc/passwd などからパスワードのハッシュ値を読み取り、パスワードをcrypt()でハッシュ値にし、/etc/passwd 等から得たハッシュ値と一致するかを調べます。 全く同じ値になれば、本人確認は成功したことになります。同じ値にならなければ、認証失敗になります。

パスワードを登録する場合は、 crypt (ユーザの入力したパスワード, システムで生成した乱数のsalt) で計算します。 パスワードが登録されているユーザを認証する場合には、 crypt(ユーザの入力したパスワード, システム内に登録されているパスワードハッシュの文字列) を計算し、システム内に登録されているパスワードハッシュの文字列を一致するかを確認します。なぜ、cryptに「システム内に登録されているパスワードハッシュの文字列」を渡すかというと、その中に「システムで生成した乱数のsalt」が含まれているからです。同じパスワードハッシュを計算するためには、同じ値の salt でなければ、同じハッシュは生成されません。

扱えるアルゴリズム

アルゴリズムは、時代に応じて変わりますが、以下のアルゴリズムは利用できます。

  1. MD5
  2. Blowfish
  3. NT‐Hash
  4. (unused)
  5. SHA‐256
  6. 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() では、以下のようなことが行われます。

  1. 先頭に $6$ があるかどうかチェックし、存在すれば、その分を進めたポインタを salt に格納します。
  2. salt に rounds= のプレフィックスがあるか確認します。rounds=があれば、ラウンドの値を srounds に取り出します。salt のポインタを rounds=値$ 分を進めます。
  3. salt の長さを計算します。
  4. key の長さを計算します。
  5. SHA512_Init() で初期化します。
  6. SHA512_Update に key を与えます。
  7. SHA512_Update に salt を与えます。
  8. SHA512_Update に key を与えます。
  9. SHA512_Final で結果を取り出します(alt_result)。
  10. key が 64 をより長ければ、 SHA512_Update に alt_result を与え、 key から 64 を引き、さらに 64より大きければ、同じ処理を繰り返します。
  11. SHA-512_Update に alt_result を与えます。
  12. key の長さに応じて、さらに SHA512_Updateに alt_result を64文字与えたり、 key を与えます。
  13. SHA_Final で結果を取り出します(alt_result)。これは、中間結果です。
  14. SHA512_Init() で初期化します。
  15. key の長さの回数まで、 SHA512_Update に key を繰り返し、与えます。
  16. SHA512_Final で ダイジェストを取り出します(temp_result)。
  17. バイトシーケンス P を作成します。 memcpy で、 temp_result をコピーします。key の長さで 64の単位でコピーされます。
  18. バイトシーケンス S を作成します。P と同じ方法で作成されます。
  19. CPU サイクルを焼き尽くすために、SHA512を使って、実行を繰り返します。
    1. rounds で定義されている回数だけ、繰り返します。
    2. カウンタの値によって、処理が変わります。 P や S, alt_result などを SHA512_Update に与えます。ループごとに SHA512_Init が呼ばれ、 SHA512_Final で中間結果を取り出します。
  20. パスワードフォーマットを整形します。
    1. b64_from_24bitを使って、alt_resultをbase64します。
  21. 最後に 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

関連項目




スポンサーリンク