pgcrypto

PostgreSQL 9.1.1文書
前のページ | 巻戻し | 付録 F. 追加で提供されるモジュール | 早送り | 次のページ

# F.28. pgcrypto

`pgcrypto`モジュールはPostgreSQL用の暗号関数を提供します。

## F.28.1. 汎用ハッシュ関数

### F.28.1.1. `digest()`

    digest(data text, type text) returns bytea
    digest(data bytea, type text) returns bytea

与えられた`data`のバイナリハッシュを計算します。 `type`は使用するアルゴリズムです。 標準アルゴリズムは`md5`、`sha1`、`sha224`、`sha256`、`sha384`、および`sha512`です。 `pgcrypto`がOpenSSL付きで構築された場合、表F-18で詳解する、より多くのアルゴリズムを利用することができます。

ダイジェストを16進数表記の文字列としたい場合は、結果に対して`encode()`を使用してください。 以下に例を示します。

    CREATE OR REPLACE FUNCTION sha1(bytea) returns text AS $$
        SELECT encode(digest($1, 'sha1'), 'hex')
    $$ LANGUAGE SQL STRICT IMMUTABLE;

### F.28.1.2. `hmac()`

    hmac(data text, key text, type text) returns bytea
    hmac(data bytea, key text, type text) returns bytea

`key`をキーとした`data`のハッシュ化MACを計算します。 `type`は`digest()`の場合と同じです。

`digest()`と似ていますが、ハッシュはキーを知った上で再計算されるだけです。 これは、誰かがデータを変更し、同時に一致するようにハッシュを変更するという状況を防ぎます。

キーがハッシュブロックサイズより大きい場合、まずハッシュ化され、その結果をキーとして使用します。

## F.28.2. パスワードハッシュ化関数

`crypt()`および`gen_salt()`関数は特にパスワードのハッシュ化のために設計されたものです。 `crypt()`がハッシュ処理を行い、`gen_salt()`はハッシュ処理用のアルゴリズム上のパラメータを準備します。

`crypt()`アルゴリズムは、以下の点でMD5やSHA1のような通常のハッシュ処理アルゴリズムと異なります。

1. 低速です。 データ量が少ないためパスワード総当たり攻撃に対して頑健にする唯一の方法です。

2. 結果にはソルトというランダムな値が含まれます。 このため同じパスワードのユーザでも異なった暗号化パスワードを持ちます。 これはアルゴリズムの逆処理に対する追加の防御です。

3. 結果内にアルゴリズムの種類が含まれます。 このため異なるアルゴリズムでハッシュ化したパスワードが混在可能です。

4. 一部は適応型です。 つまり、コンピュータが高速になったとしても、既存のパスワードとの互換性を損なうことなくアルゴリズムを低速に調整することができます。

`crypt()`関数がサポートするアルゴリズムを表F-15に列挙します。

表 F-15. `crypt()`がサポートするアルゴリズム

アルゴリズム | パスワード最大長 | 適応型かどうか | ソルトビット長 | 説明
--- | --- | --- | --- | ---
`bf` | 72 | はい | 128 | Blowfishベース、2a版
`md5` | 無制限 | いいえ | 48 | MD5ベースの暗号
`xdes` | 8 | はい | 24 | 拡張DES
`des` | 8 | いいえ | 12 | 元来のUNIX crypt

### F.28.2.1. `crypt()`

    crypt(password text, salt text) returns text

`password`のcrypt(3)形式のハッシュを計算します。 新しいパスワードを保管する時には、`gen_salt()`を使用して新しい`salt`を生成する必要があります。 パスワードを検査する時、既存のハッシュ値を`salt`として渡し、結果が格納された値と一致するかどうかを確認します。

新しいパスワードの設定例を以下に示します。

    UPDATE ... SET pswhash = crypt('new password', gen_salt('md5'));

認証の例です。

    SELECT pswhash = crypt('entered password', pswhash) FROM ... ;

入力パスワードが正しければ`true`を返します。

### F.28.2.2. `gen_salt()`

    gen_salt(type text [, iter_count integer ]) returns text

`crypt()`で使用するランダムなソルト文字列を新規に生成します。 また、このソルト文字列は`crypt()`にどのアルゴリズムを使用するかを通知します。

`type`パラメータはハッシュ化アルゴリズムを指定します。 受付可能な種類は、`des`、`xdes`、`md5`、`bf`です。

繰り返し回数を持つアルゴリズムでは、ユーザは`iter_count`パラメータを使用して繰り返し回数を指定できます。 指定する回数を高くすれば、パスワードのハッシュ化にかかる時間が長くなり、それを破るための時間も長くなります。 しかし、あまりに多くの回数を指定すると、ハッシュ計算にかかる時間は数年に渡ってしまう可能性があります。 これは実用的ではありません。 `iter_count`パラメータを省略した場合、デフォルトの繰り返し回数が使用されます。 `iter_count`で受け付けられる値はアルゴリズムに依存し、表F-16に示す通りです。

表 F-16. `crypt()`用の繰り返し回数

アルゴリズム | デフォルト | 最小 | 最大
--- | --- | --- | ---
`xdes` | 725 | 1 | 16777215
`bf` | 6 | 4 | 31

`xdes`の場合は他にも、回数が奇数でなければならないという制限があります。

適切な繰り返し回数を選択するために、元々のDES暗号は当時のハードウェアで1秒あたり4個のハッシュを持つことができるように設計されたことを考えてください。 毎秒4ハッシュより遅いと、おそらく使い勝手が悪いでしょう。 毎秒100ハッシュより速いというのは、十中八九、あまりにも速すぎるでしょう。

ハッシュ化アルゴリズム別に相対的な速度に関する概要を表F-17にまとめました。 この表は、8文字のパスワード内のすべての文字の組合せを取るためにかかる時間を示します。 また、すべて小文字の英字のみのパスワードである場合と大文字小文字が混在した英字と数字のパスワードの場合を仮定します。 `crypt-bf`の項では、スラッシュの後の数値は`gen_salt`の`iter_count`です。

表 F-17. ハッシュアルゴリズムの速度

アルゴリズム | 1秒当たりのハッシュ数 | `[a-z]`の場合 | `[A-Za-z0-9]`の場合
--- | --- | --- | ---
`crypt-bf/8` | 28 | 246年 | 251322年
`crypt-bf/7` | 57 | 121年 | 123457年
`crypt-bf/6` | 112 | 62年 | 62831年
`crypt-bf/5` | 211 | 33年 | 33351年
`crypt-md5` | 2681 | 2.6年 | 2625年
`crypt-des` | 362837 | 7日 | 19年
`sha1` | 590223 | 4日 | 12年
`md5` | 2345086 | 1日 | 3年

注意：

- Pentium 4 1.5GHzのマシンを使用しました。

- `crypt-des`および`crypt-md5`アルゴリズムの数値はJohn the Ripper v1.6.38の`-test`出力から得たものです。

- `md5`の数値はmdcrack 1.2のものです。

- `sha1`の数値はlcrack-20031130-betaのものです。

- `crypt-bf`の数は、1000個の8文字パスワードをループする単純なプログラムを使用して得たものです。 こうして、異なる回数の速度を示すことができました。 参考までに、`john -test`は`crypt-bf/5`で213 loops/secでした。 （結果の差異が非常に小さいことは、`pgcrypto`における`crypt-bf`実装がJohn the Ripperで使用されるものと同じであるという事実と一致します。）

"すべての組み合わせを試行する"ことは現実的な行使ではありません。 通常パスワード推定は、普通の単語とその変形の両方を含む辞書を使用して行われます。 ですので、いささかなりとも言葉に似たパスワードは上で示した数値よりも速く推定されます。 また6文字の単語に似ていないパスワードは推定を免れるかもしれませんし、免れないかもしれません

## F.28.3. PGP暗号化関数

ここで示す関数はOpenPGP（RFC 4880）標準の暗号処理部分を実装します。 対称キーおよび公開キー暗号化がサポートされます。

暗号化されたPGPメッセージは次の2つの部品（またはパケット）から構成されます。

- セッションキーを含むパケット。 対称キーまたは公開キー暗号化の両方。

- セッションキーにより暗号化されたデータを含むパケット。

対称キー（つまりパスワード）で暗号化する場合

1. 与えられたパスワードはString2Key(S2K)アルゴリズムでハッシュ化されます。 これはどちらかというと`crypt()`アルゴリズムと似て、意図的に低速で、かつランダムなソルトを使用します。 しかし、全長のバイナリキーを生成します。

2. 分離したセッションキーが要求された場合、新しいランダムなキーが生成されます。 さもなくば、S2Kキーがそのままセッションキーとして使用されます。

3. S2Kキーがそのまま使用される場合、S2K設定のみがセッションキーパケットに格納されます。 さもなくば、セッションキーはS2Kキーで暗号化され、セッションキーパケットに格納されます。

公開キーで暗号化する場合

1. 新しいランダムなセッションキーが生成されます。

2. これは公開キーを使用して暗号化され、セッションキーパケットに格納されます。

どちらの場合でもデータ暗号化は以下のように処理されます。

1. 省略可能なデータ操作として、圧縮、UTF-8への変換、改行の変換があります。

2. データの前にはランダムなバイト数のブロックが付きます。 これはrandom IVを使用する場合と同じです。

3. ランダムな前置ブロックとデータのSHA1ハッシュが後に付けられます。

4. これをすべてセッションキーで暗号化し、データパケットに格納します。

### F.28.3.1. `pgp_sym_encrypt()`

    pgp_sym_encrypt(data text, psw text [, options text ]) returns bytea
    pgp_sym_encrypt_bytea(data bytea, psw text [, options text ]) returns bytea

対称PGPキー`psw`で`data`を暗号化します。 `options`パラメータには後述のオプション設定を含めることができます。

### F.28.3.2. `pgp_sym_decrypt()`

    pgp_sym_decrypt(msg bytea, psw text [, options text ]) returns text
    pgp_sym_decrypt_bytea(msg bytea, psw text [, options text ]) returns bytea

対称キーで暗号化されたPGPメッセージを復号化します。

`pgp_sym_decrypt`で`bytea`型のデータを復号化することはできません。 これは無効な文字データの出力を防止するためです。 元のテキストのデータを`pgp_sym_decrypt_bytea`で復号化することが正しい方法です。

`options`パラメータには後述のオプション設定を含めることができます。

### F.28.3.3. `pgp_pub_encrypt()`

    pgp_pub_encrypt(data text, key bytea [, options text ]) returns bytea
    pgp_pub_encrypt_bytea(data bytea, key bytea [, options text ]) returns bytea

公開PGPキー`key`で`data`を暗号化します。 この関数に秘密キーを与えるとエラーになります。

`options`パラメータには後述のオプション設定を含めることができます。

### F.28.3.4. `pgp_pub_decrypt()`

    pgp_pub_decrypt(msg bytea, key bytea [, psw text [, options text ]]) returns text
    pgp_pub_decrypt_bytea(msg bytea, key bytea [, psw text [, options text ]]) returns bytea

公開キーで暗号化されたメッセージを復号化します。 `key`は、暗号化に使用した公開キーに対応する秘密キーでなければなりません。 秘密キーがパスワードで保護されている場合は、そのパスワードを`psw`で指定しなければなりません。 パスワードはないが、オプションを指定したい場合は空のパスワードを指定する必要があります。

`pgp_pub_decrypt`で`bytea`型のデータを復号化することはできません。 これは無効な文字データの出力を防止するためです。 元のテキストのデータを`pgp_pub_decrypt_bytea`で復号化することが正しい方法です。

`options`パラメータには後述のオプション設定を含めることができます。

### F.28.3.5. `pgp_key_id()`

    pgp_key_id(bytea) returns text

`pgp_key_id`はPGP公開キーまたは秘密キーのキーIDを取り出します。 暗号化されたメッセージが指定された場合は、データの暗号化に使用されたキーIDを与えます。

2つの特殊なキーIDを返すことがあります。

- `SYMKEY`

  メッセージは対称キーで暗号化されました。

- `ANYKEY`

  メッセージは公開キーで暗号化されましたが、キーIDが消去されていました。 つまり、どれで復号化できるかを判定するためにはすべての秘密キーを試行しなければならないことを意味します。 `pgcrypto`自身はこうしたメッセージを生成しません。

異なるキーが同一IDを持つ場合があることに注意してください。 これは稀ですが、正常なイベントです。 この場合クライアントアプリケーションはどちらが当てはまるかを調べるために、`ANYKEY`の場合と同様に、それぞれのキーで復号化を試行しなければなりません。

### F.28.3.6. `armor()`, `dearmor()`

    armor(data bytea) returns text
    dearmor(data text) returns bytea

PGPのASCIIアーマー形式にデータを隠す、または、データを取り出します。 ASCIIアーマーは基本的にCRC付きのBASE64という形式で、追加のフォーマットがあります。

### F.28.3.7. PGP関数用のオプション

オプションはGnuPGに似せて命名しています。 オプションの値は等号記号の後に指定しなければなりません。 複数のオプションはカンマで区切ってください。 以下に例を示します。

    pgp_sym_encrypt(data, psw, 'compress-algo=1, cipher-algo=aes256')

`convert-crlf`を除くすべてのオプションは暗号化関数にのみ適用可能です。 復号化関数はPGPデータからこうしたパラメータを入手します。

もっとも興味深いオプションはおそらく`compress-algo`と`unicode-mode`でしょう。 残りはデフォルトで問題ないはずです。

#### F.28.3.7.1. cipher-algo

使用する暗号アルゴリズム。

値: bf, aes128, aes192, aes256 (OpenSSL-only: `3des`, `cast5`)
デフォルト: aes128
適用範囲: pgp_sym_encrypt, pgp_pub_encrypt

#### F.28.3.7.2. compress-algo

使用する圧縮アルゴリズム。 PostgreSQLがzlib付きで構築されている場合のみ利用可能です。

値:
0 - 非圧縮
1 - ZIP圧縮
2 - ZLIB圧縮 (ZIPにメタデータとブロックCRCを加えたもの)
デフォルト: 0
適用範囲: pgp_sym_encrypt, pgp_pub_encrypt

#### F.28.3.7.3. compress-level

どの程度圧縮するかです。 レベルが大きい程小さくなりますが、低速になります。 0は圧縮を無効にします。

値: 0, 1-9
デフォルト: 6
適用範囲: pgp_sym_encrypt, pgp_pub_encrypt

#### F.28.3.7.4. convert-crlf

暗号化の際に`\n`を`\r\n`に、復号化の際に`\r\n` を`\n` に変換するかどうか。 RFC 4880では、テキストデータは改行コードとして`\r\n` を使用して格納すべきであると規定されています。 完全にRFC準拠の動作を行いたければ、これを使用してください。

値: 0, 1
デフォルト: 0
適用範囲: pgp_sym_encrypt, pgp_pub_encrypt, pgp_sym_decrypt, pgp_pub_decrypt

#### F.28.3.7.5. disable-mdc

データをSHA-1で保護しません。 このオプションを使用することが良い唯一の理由は、SHA-1で保護されたパケットがRFC 4880に追加される前の、古いPGP製品との互換性を得るためです。 最近のgnupg.orgおよびpgp.comのソフトウェアではこれを正しくサポートしています。

値: 0, 1
デフォルト: 0
適用範囲: pgp_sym_encrypt, pgp_pub_encrypt

#### F.28.3.7.6. enable-session-key

分離したセッションキーを使用します。 公開キー暗号では常に分離したセッションキーを使用します。 これは対称キー暗号向けのもので、デフォルトではS2Kをそのまま使用します。

値: 0, 1
デフォルト: 0
適用範囲: pgp_sym_encrypt

#### F.28.3.7.7. s2k-mode

使用するS2Kアルゴリズム。

値:
0 - ソルト無。危険です
1 - ソルト有。固定繰り返し回数。
3 - 可変繰り返し回数
デフォルト: 3
適用範囲: pgp_sym_encrypt

#### F.28.3.7.8. s2k-digest-algo

S2K計算で使用するダイジェストアルゴリズム。

値: md5, sha1
デフォルト: sha1
適用範囲: pgp_sym_encrypt

#### F.28.3.7.9. s2k-cipher-algo

分離したセッションキーの暗号化に使用する暗号。

値: bf, aes, aes128, aes192, aes256
デフォルト: cipher-algoを使用
適用範囲: pgp_sym_encrypt

#### F.28.3.7.10. unicode-mode

テキストデータをデータベース内部符号化方式からUTF-8に変換して戻すかどうかです。 データベースがすでにUTF-8であれば、変換は起こらず、データにUTF-8としてタグが付くのみです。 このオプションがないと、何も行われません。

値: 0, 1
デフォルト: 0
適用範囲: pgp_sym_encrypt, pgp_pub_encrypt

### F.28.3.8. GnuPGを使用したキーの生成

新しいキーを生成します。

    gpg --gen-key

推奨するキー種類は"DSA and Elgamal"です。

RSA暗号化のためには、マスタとしてDSAまたはRSAで署名のみのキーを作成し、そして`gpg --edit-key`を使用してRSAで暗号化された副キーを追加しなければなりません

キーを列挙します。

    gpg --list-secret-keys

ASCIIアーマー形式で公開キーをエキスポートします。

    gpg -a --export KEYID > public.key

ASCIIアーマー形式の秘密キーをエキスポートします。

    gpg -a --export-secret-keys KEYID > secret.key

PGP関数にこれらのキーを渡す前に`dearmor()``を使用する必要があります。 バイナリデータを扱うことができる場合、gpgから`-a`フラグを省略することができます。

詳細は`man gpg`、http://www.gnupg.org/gph/en/manual.htmlThe GNU Privacy Handbook、http://www.gnupg.orgサイトの各種文書を参照してください。

### F.28.3.9. PGPコードの制限

- 署名に関するサポートはありません。 これはまた、暗号化副キーがマスタキーに属しているかどうか検査しないことを意味します。

- マスタキーとして暗号化キーをサポートしません。 一般的にこうした状況は現実的ではありませんので、問題にならないはずです。

- 複数の副キーに関するサポートはありません。 よくありますので、これは問題になりそうに見えます。 一方、通常のGPG/PGPキーを`pgcrypto`で使用すべきではありません。 使用する状況が多少異なりますので新しく作成してください。

## F.28.4. 暗号化そのものを行う関数

これらの関数はデータ全体を暗号化するためだけに実行します。 PGP暗号化の持つ先端的な機能はありません。 したがって、大きな問題がいくつか存在します。

1. 暗号キーとしてユーザキーをそのまま使用します。

2. 暗号化されたデータが変更されたかどうかを確認するための整合性検査をまったく提供しません。

3. ユーザが、IVをも含め暗号化パラメータ自体をすべて管理していることを想定しています。

4. テキストは扱いません。

このため、PGP暗号化の導入もあり、暗号化のみの関数はあまり使用されません。

    encrypt(data bytea, key bytea, type text) returns bytea
    decrypt(data bytea, key bytea, type text) returns bytea

    encrypt_iv(data bytea, key bytea, iv bytea, type text) returns bytea
    decrypt_iv(data bytea, key bytea, iv bytea, type text) returns bytea

`type`で指定した暗号方法を使用してデータの暗号化・復号化を行います。 `type`文字列の構文は以下の通りです。

    algorithm [ - mode ] [ /pad: padding ]

ここで`algorithm`は以下の1つです。

- `bf` — Blowfish

- `aes` — AES (Rijndael-128)

また`mode`は以下の1つです。

- `cbc` — 次のブロックは前ブロックに依存します（デフォルト）

- `ecb` — 各ブロックは独自に暗号化されます（試験用途のみ）

`padding`は以下の1つです。

- `pkcs` — データ長に制限はありません（デフォルト）

- `none` — データは暗号ブロックサイズの倍数でなければなりません

このため、例えば以下は同じです。

    encrypt(data, 'fooz', 'bf')
    encrypt(data, 'fooz', 'bf-cbc/pad:pkcs')

`encrypt_iv`および`decrypt_iv`では、`iv`パラメータはCBCモード用の初期値となります。 ECBでは無視されます。 正確にブロック長でない場合、切り抜かれるか、もしくはゼロで埋められます。 このパラメータがない場合、関数のデフォルト値はすべてゼロです。

## F.28.5. ランダムデータ関数

    gen_random_bytes(count integer) returns bytea

暗号論的に強いランダムな`count`バイトを返します。 一度に最大で1024バイトを抽出することができます。 ランダム性ジェネレータプールを空にすることを防止するためのものです。

## F.28.6. 注釈

### F.28.6.1. 構築

`pgcrypto`は自身で主PostgreSQLの`configure`スクリプトの検出結果に従って構築します。 構築に影響するオプションは`--with-zlib`と`--with-openssl`です。

ZLIB付きでコンパイルされた場合、PGP暗号化関数は暗号化前にデータを圧縮することができます。

OpenSSL付きでコンパイルされた場合、より多くのアルゴリズムが利用できるようになります。 また、OpenSSLがより最適化されたBIGNUM関数を持つため、公開キー暗号化関数は高速になります。

表 F-18. OpenSSLの有無による機能のまとめ

機能 | 組込み | OpenSSL付き
--- | --- | ---
MD5 | ○ | ○
SHA1 | ○ | ○
SHA224/256/384/512 | ○ | ○（注１）
この他のダイジェストアルゴリズム | × | ○（注２）
Blowfish | ○ | ○
AES | ○ | ○（注３）
DES/3DES/CAST5 | × | ○
暗号化そのもの | ○ | ○
PGP対称暗号化 | ○ | ○
PGP公開キー暗号化 | ○ | ○

注

1. SHA2はOpenSSLバージョン0.9.8から含まれています。 これより古いバージョンでは、`pgcrypto`は組込みのコードを使用します。

2. OpenSSLがサポートする任意のダイジェストアルゴリズムが自動的に選択されます。 これは、明示的にサポートされなければならない暗号では使用できません。

3. AESはOpenSSLバージョン0.9.7から含まれています。 これより古いバージョンでは、`pgcrypto`は組込みのコードを使用します。

### F.28.6.2. NULLの扱い

標準SQLの通り、引数のいずれかがNULLの場合、すべての関数はNULLを返します。 注意せずに使用すると、これがセキュリティ上の問題になるかもしれません。

### F.28.6.3. セキュリティ上の制限

`pgcrypto`の関数はすべてデータベースサーバ内部で実行されます。 これは、`pgcrypto`とクライアントアプリケーションとの間でやり取りされるデータはすべて平文であることを意味します。 したがって、以下を行う必要があります。

1. ローカルまたはSSL接続で接続

2. システム管理者およびデータベース管理者を信頼

これらが不可能であれば、クライアントアプリケーション内で暗号化する方が望まれます。

### F.28.6.4. 有用な文書

- http://www.gnupg.org/gph/en/manual.html

  GNUプライバシーハンドブック

- http://www.openwall.com/crypt/

  blowfish暗号アルゴリズムの説明

- http://www.stack.nl/~galactus/remailers/passphrase-faq.html

  優れたパスワードの選び方

- http://world.std.com/~reinhold/diceware.html

  パスワード決定に関する面白い考え

- http://www.interhack.net/people/cmcurtin/snake-oil-faq.html

  良い暗号、悪い暗号に関する説明

### F.28.6.5. 技術的な参考情報

- http://www.ietf.org/rfc/rfc4880.txt

  OpenPGPメッセージフォーマット

- http://www.ietf.org/rfc/rfc1321.txt

  MD5 メッセージダイジェストアルゴリズム

- http://www.ietf.org/rfc/rfc2104.txt

  HMAC: Keyed-Hashing for Message Authentication.

- http://www.usenix.org/events/usenix99/provos.html

  DES暗号、MD5暗号、bcryptアルゴリズムの比較

- http://csrc.nist.gov/cryptval/des.htm

  DES、3DES、AES標準

- http://en.wikipedia.org/wiki/Fortuna_(PRNG)

  Fortuna CSPRNGの説明

- http://jlcooke.ca/random/

  Linux用Jean-Luc Cooke Fortunaに基づく`/dev/random`ドライバ

- http://research.cyber.ee/~lipmaa/crypto/

  暗号学に関するリンク集

## F.28.7. 作者

Marko Kreen `<markokr@gmail.com>`

`pgcrypto`は以下のソースを使用しています。

アルゴリズム | 作者 | 元ソース
--- | --- | ---
DES crypt | David Burren and others | FreeBSD libcrypt
MD5 crypt | Poul-Henning Kamp | FreeBSD libcrypt
Blowfish crypt | Solar Designer | www.openwall.com
Blowfish cipher | Simon Tatham | PuTTY
Rijndael cipher | Brian Gladman | OpenBSD sys/crypto
MD5 and SHA1 | WIDE Project | KAME kame/sys/crypto
SHA256/384/512 | Aaron D. Gifford | OpenBSD sys/crypto
BIGNUM math | Michael J. Fromberger | dartmouth.edu/~sting/sw/imath

前のページ | ホーム | 次のページ
pg_buffercache | 上に戻る | pg_freespacemap