F.34. pgcrypto — криптографические функции#

F.34. pgcrypto — криптографические функции

F.34. pgcrypto — криптографические функции #

Модуль pgcrypto предоставляет криптографические функции для Tantor BE.

Этот модуль считается "доверенным", то есть его можно установить недоступным пользователям, у которых есть привилегия CREATE в текущей базе данных.

pgcrypto требует наличия OpenSSL и не будет установлено, если поддержка OpenSSL не была выбрана при сборке PostgreSQL.

F.34.1. Общие функции хеширования #

F.34.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. Кроме того, автоматически выбирается любой алгоритм хеширования, поддерживаемый OpenSSL.

Если нужно получить дайджест в виде шестнадцатеричной строки, используйте функцию encode() для полученного результата. Например:

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

F.34.1.2. hmac() #

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

Вычисляет хешированный MAC для data с ключом key. type такой же, как в digest().

Это похоже на функцию digest(), но хеш можно пересчитать только зная ключ. Это предотвращает ситуацию, когда кто-то изменяет данные и также изменяет хеш, чтобы он совпадал.

Если ключ больше размера блока хеша, он будет сначала хеширован, а затем результат будет использоваться в качестве ключа.

F.34.2. Функции хеширования паролей #

Функции crypt() и gen_salt() специально разработаны для хеширования паролей. crypt() выполняет хеширование, а gen_salt() подготавливает параметры алгоритма для него.

Алгоритмы в функции crypt() отличаются от обычных алгоритмов хеширования MD5 или SHA1 следующими особенностями:

  1. Они медленные. Поскольку объем данных настолько мал, это единственный способ сделать подбор паролей сложным.

  2. Они используют случайное значение, называемое соль, чтобы пользователи с одинаковым паролем имели разные зашифрованные пароли. Это также дополнительная защита от обратного преобразования алгоритма.

  3. Они включают тип алгоритма в результат, поэтому пароли, хешированные с использованием разных алгоритмов, могут сосуществовать.

  4. Некоторые из них адаптивные — это означает, что при увеличении скорости компьютеров вы можете настроить алгоритм на более медленную работу, не внося несовместимости с существующими паролями.

Таблица F.18 перечисляет алгоритмы, поддерживаемые функцией crypt().

Таблица F.18. Поддерживаемые алгоритмы для функции crypt()

АлгоритмМаксимальная длина пароляАдаптивный?Биты солиДлина выводаОписание
bf72yes12860Основанный на Blowfish, вариант 2a
md5неограниченныйнет4834криптография на основе MD5
xdes8yes2420Расширенный DES
des8no1213Оригинальное UNIX-шифрование

F.34.2.1. crypt() #

crypt(password text, salt text) returns text

Вычисляет хеш в стиле crypt(3) для password. При сохранении нового пароля необходимо использовать функцию gen_salt() для генерации нового значения salt. Для проверки пароля передайте сохраненное значение хеша в качестве salt и проверьте, совпадает ли результат с сохраненным значением.

Пример установки нового пароля:

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

Пример аутентификации:

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

Это возвращает true, если введенный пароль правильный.

F.34.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.19.

Таблица F.19. Количество итераций для crypt()

АлгоритмПо умолчаниюМинимумМаксимум
xdes725116777215
bf6431

Для xdes существует дополнительное ограничение: количество итераций должно быть нечетным числом.

Для выбора подходящего количества итераций рассмотрите, что исходный DES crypt был разработан для обеспечения скорости выполнения 4 хешей в секунду на аппаратуре того времени. Скорость медленнее 4 хешей в секунду, вероятно, снизит удобство использования. Скорость быстрее 100 хешей в секунду, вероятно, слишком высока.

Таблица F.20 дает обзор относительной медлительности различных алгоритмов хеширования. Таблица показывает, сколько времени потребуется для попытки всех комбинаций символов в пароле из 8 символов, предполагая, что пароль содержит либо только строчные буквы, либо строчные и заглавные буквы и цифры. В записях crypt-bf, число после слэша - параметр iter_count функции gen_salt.

Таблица F.20. Скорости хеш-алгоритмов

АлгоритмХеши/секДля [a-z]Для [A-Za-z0-9]Продолжительность относительно md5 hash
crypt-bf/817924 года3927 лет100k
crypt-bf/736482 года1929 года50k
crypt-bf/671681 год982 года25k
crypt-bf/513504188 дней521 лет12.5k
crypt-md517158415 дней41 год
crypt-des23221568157.5 минуты108 дней7
sha13777427290 минут68 дней4
md5 (хеш)15008550422.5 минуты17 дней1

Примечания:

  • Используемая машина - Intel Mobile Core i3.

  • crypt-des и crypt-md5 номера алгоритмов взяты из вывода John the Ripper v1.6.38 -test.

  • md5 hash числа взяты из mdcrack 1.2.

  • sha1 числа взяты из lcrack-20031130-beta.

  • crypt-bf числа взяты с использованием простой программы, которая выполняет цикл из 1000 8-символьных паролей. Таким образом, можно показать скорость при различных количествах итераций. Для справки: john -test показывает 13506 циклов/сек для crypt-bf/5. (Очень небольшая разница в результатах соответствует тому факту, что реализация crypt-bf в pgcrypto такая же, как используется в John the Ripper.)

Обратите внимание, что попробовать все комбинации не является реалистичным упражнением. Обычно взлом паролей выполняется с помощью словарей, которые содержат как обычные слова, так и различные их мутации. Таким образом, даже пароли, напоминающие слова, могут быть взломаны намного быстрее, чем указанные выше числа, в то время как 6-символьный пароль, не похожий на слово, может остаться невзломанным. Или нет.

F.34.3. Функции шифрования PGP #

Функции здесь реализуют часть шифрования OpenPGP (RFC 4880) стандарта. Поддерживаются как симметричное, так и асимметричное шифрование.

Зашифрованное сообщение PGP состоит из 2 частей, или пакетов:

  • Пакет, содержащий ключ сессии — либо симметрично-ключевой, либо зашифрованный с использованием открытого ключа.

  • Пакет, содержащий данные, зашифрованные сессионным ключом.

При шифровании с использованием симметричного ключа (т.е. пароля):

  1. Предоставленный пароль хешируется с использованием алгоритма String2Key (S2K). Это довольно похоже на алгоритмы crypt() - намеренно медленные и с случайной солью - но оно создает полноценный двоичный ключ.

  2. Если запрашивается отдельный ключ сессии, будет сгенерирован новый случайный ключ. В противном случае ключ S2K будет использоваться напрямую в качестве ключа сессии.

  3. Если ключ S2K должен использоваться напрямую, то в пакет ключа сессии будут включены только настройки S2K. В противном случае ключ сессии будет зашифрован с помощью ключа S2K и помещен в пакет ключа сессии.

При шифровании с помощью открытого ключа:

  1. Генерируется новый случайный ключ сессии.

  2. Он шифруется с использованием открытого ключа и помещается в пакет сессионного ключа.

В любом случае данные, которые должны быть зашифрованы, обрабатываются следующим образом:

  1. Дополнительная обработка данных: сжатие, преобразование в UTF-8 и/или преобразование символов конца строки.

  2. Данные предваряются блоком случайных байтов. Это эквивалентно использованию случайного инициализационного вектора (IV).

  3. В конец добавляется SHA1-хеш случайного префикса и данных.

  4. Весь этот текст зашифрован сессионным ключом и помещен в пакет данных.

F.34.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

Зашифруйте data симметричным PGP ключом psw. Параметр options может содержать настройки параметров, как описано ниже.

F.34.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-сообщение.

Расшифровка данных типа bytea с использованием функции pgp_sym_decrypt запрещена. Это сделано для предотвращения вывода недопустимых символьных данных. Расшифровка исходно текстовых данных с использованием функции pgp_sym_decrypt_bytea допустима.

Параметр options может содержать настройки опций, как описано ниже.

F.34.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

Шифрование data с использованием открытого PGP-ключа key. Если передать этой функции секретный ключ, будет сгенерирована ошибка.

Параметр options может содержать настройки опций, как описано ниже.

F.34.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. Если пароля нет, но нужно указать параметры, вам нужно указать пустой пароль.

Расшифровка данных типа bytea с использованием функции pgp_pub_decrypt запрещена. Это сделано для предотвращения вывода недопустимых символьных данных. Расшифровка исходно текстовых данных с использованием функции pgp_pub_decrypt_bytea допустима.

Параметр options может содержать настройки опций, как описано ниже.

F.34.3.5. pgp_key_id() #

pgp_key_id(bytea) returns text

pgp_key_id извлекает идентификатор ключа открытого или секретного ключа PGP. Или он дает идентификатор ключа, который был использован для шифрования данных, если дано зашифрованное сообщение.

Он может возвращать 2 специальных идентификатора ключей:

  • SYMKEY

    Сообщение зашифровано симметричным ключом.

  • ANYKEY

    Сообщение зашифровано с использованием открытого ключа, но идентификатор ключа был удален. Это означает, что вам нужно попробовать все ваши секретные ключи, чтобы узнать, какой из них расшифровывает его. Сам pgcrypto не генерирует такие сообщения.

Обратите внимание, что разным ключам может быть присвоен один и тот же идентификатор. Это редкое, но нормальное событие. Клиентское приложение должно попытаться расшифровать каждый ключ, чтобы узнать, какой подходит - подобно обработке ANYKEY.

F.34.3.6. armor(), dearmor() #

armor(data bytea [ , keys text[], values text[] ]) returns text
dearmor(data text) returns bytea

Эти функции оборачивают/распаковывают двоичные данные в формат PGP ASCII-armor, который в основном представляет собой Base64 с контрольной суммой CRC и дополнительным форматированием.

Если указаны массивы keys и values, к каждой паре ключ/значение в зашифрованном формате добавляется заголовок брони. Оба массива должны быть одномерными и иметь одинаковую длину. Ключи и значения не могут содержать не-ASCII символы.

F.34.3.7. pgp_armor_headers #

pgp_armor_headers(data text, key out text, value out text) returns setof record

pgp_armor_headers() извлекает заголовки брони из data. Возвращаемое значение - набор строк с двумя столбцами, ключ и значение. Если ключи или значения содержат любые не-ASCII символы, они рассматриваются как UTF-8.

F.34.3.8. Опции для функций PGP #

Опции названы так, чтобы быть похожими на GnuPG. Значение опции должно быть указано после знака равенства; разделяйте опции друг от друга запятыми. Например:

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

Все параметры, кроме convert-crlf, применяются только к функциям шифрования. Функции дешифрования получают параметры из данных PGP.

Самые интересные параметры, вероятно, это compress-algo и unicode-mode. Остальные должны иметь разумные значения по умолчанию.

F.34.3.8.1. cipher-algo #

Какой алгоритм шифрования использовать.

Значения: bf, aes128, aes192, aes256, 3des, cast5
По умолчанию: aes128
Применяется к: pgp_sym_encrypt, pgp_pub_encrypt

F.34.3.8.2. compress-algo #

Какой алгоритм сжатия использовать. Доступен только если Tantor BE был собран с использованием zlib.

Значения:
  0 - без сжатия
  1 - сжатие ZIP
  2 - сжатие ZLIB (= ZIP плюс метаданные и блочные CRC)
По умолчанию: 0
Применяется к: pgp_sym_encrypt, pgp_pub_encrypt

F.34.3.8.3. compress-level #

На сколько сжимать. Более высокие уровни сжимают меньше, но работают медленнее. 0 отключает сжатие.

Значения: 0, 1-9
По умолчанию: 6
Применяется к: pgp_sym_encrypt, pgp_pub_encrypt

F.34.3.8.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.34.3.8.5. disable-mdc #

Не защищайте данные с помощью SHA-1. Единственная хорошая причина использовать эту опцию - достижение совместимости с древними продуктами PGP, предшествующими добавлению защищенных пакетов SHA-1 в RFC 4880. Недавнее программное обеспечение gnupg.org и pgp.com отлично поддерживает его.

Значения: 0, 1
По умолчанию: 0
Применяется к: pgp_sym_encrypt, pgp_pub_encrypt

F.34.3.8.6. sess-key #

Используйте отдельный ключ сессии. Шифрование с открытым ключом всегда использует отдельный ключ сессии; эта опция предназначена для симметричного шифрования, которое по умолчанию использует ключ S2K напрямую.

Значения: 0, 1
По умолчанию: 0
Применяется к: pgp_sym_encrypt

F.34.3.8.7. s2k-mode #

Какой алгоритм S2K использовать.

Значения:
  0 - Без соли. Опасно!
  1 - С солью, но с фиксированным количеством итераций.
  3 - Переменное количество итераций.
По умолчанию: 3
Применяется к: pgp_sym_encrypt

F.34.3.8.8. счетчик-с2к #

Количество итераций алгоритма S2K, которое будет использоваться. Оно должно быть значением от 1024 до 65011712 включительно.

По умолчанию: случайное значение между 65536 и 253952
Применяется к: pgp_sym_encrypt, только с s2k-mode=3

F.34.3.8.9. s2k-digest-algo #

Какой алгоритм хеширования использовать при вычислении S2K.

Значения: md5, sha1
По умолчанию: sha1
Применяется к: pgp_sym_encrypt

F.34.3.8.10. s2k-cipher-algo #

Какой шифр использовать для шифрования отдельного ключа сессии.

Значения: bf, aes, aes128, aes192, aes256
По умолчанию: используйте cipher-algo
Применяется к: pgp_sym_encrypt

F.34.3.8.11. unicode-mode #

Определяет, нужно ли преобразовывать текстовые данные из внутренней кодировки базы данных в UTF-8 и обратно. Если ваша база данных уже использует UTF-8, преобразование не будет выполняться, но сообщение будет помечено как UTF-8. Без этой опции это не произойдет.

Значения: 0, 1
По умолчанию: 0
Применяется к: pgp_sym_encrypt, pgp_pub_encrypt

F.34.3.9. Генерация PGP-ключей с помощью GnuPG #

Для генерации нового ключа:

gpg --gen-key

Предпочтительный тип ключа - DSA и Elgamal.

Для шифрования RSA необходимо создать ключ DSA или RSA только для подписи в качестве основного, а затем добавить подключ RSA для шифрования с помощью gpg --edit-key.

Для перечисления ключей:

gpg --list-secret-keys

Для экспорта открытого ключа в формате ASCII-armor:

gpg -a --export KEYID > public.key

Чтобы экспортировать секретный ключ в формате ASCII-armor:

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

Вам нужно использовать dearmor() для этих ключей перед передачей их функциям PGP. Или, если вы можете работать с двоичными данными, вы можете опустить -a из команды.

Для получения более подробной информации см. man gpg, Руководство по безопасности GNU и другую документацию на https://www.gnupg.org/.

F.34.3.10. Ограничения кода PGP #

  • Нет поддержки подписи. Это также означает, что не проверяется, принадлежит ли шифровальный подключ к основному ключу.

  • Нет поддержки ключа шифрования в качестве главного ключа. Поскольку такая практика обычно не рекомендуется, это не должно быть проблемой.

  • Нет поддержки нескольких подключей. Это может показаться проблемой, так как это общая практика. С другой стороны, вы не должны использовать свои обычные GPG/PGP ключи с pgcrypto, а должны создать новые, так как сценарий использования здесь совершенно иной.

F.34.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 является одним из:

  • bf — Blowfish

  • aes — AES (Rijndael-128, -192 или -256)

и mode является одним из:

  • cbc — следующий блок зависит от предыдущего (по умолчанию)

  • ecb — каждый блок шифруется отдельно (только для тестирования)

и padding является одним из:

  • pkcs — данные могут иметь любую длину (по умолчанию)

  • none — данные должны быть кратны размеру блока шифрования

Так, например, эти два выражения эквивалентны:

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

В функциях encrypt_iv и decrypt_iv параметр iv является начальным значением для режима CBC; в режиме ECB он игнорируется. Если значение не соответствует размеру блока, оно обрезается или дополняется нулями. По умолчанию в функциях без этого параметра используется значение всех нулей.

F.34.5. Функции для генерации случайных данных #

gen_random_bytes(count integer) returns bytea

Возвращает криптографически надежные случайные байты count. За один раз можно извлечь не более 1024 байт. Это сделано для предотвращения истощения пула генератора случайных чисел.

gen_random_uuid() returns uuid

Возвращает версию 4 (случайный) UUID. (Устарело, эта функция внутренне вызывает ядреную функцию того же имени).

F.34.6. Примечания #

F.34.6.1. Конфигурация #

pgcrypto настраивается в соответствии с результатами выполнения основного сценария configure в PostgreSQL. Опции, которые на него влияют, - это --with-zlib и --with-ssl=openssl.

Когда компилируется с zlib, функции шифрования PGP могут сжимать данные перед шифрованием.

pgcrypto требует OpenSSL. В противном случае, он не будет собран или установлен.

При компиляции с использованием OpenSSL версии 3.0.0 и выше, для использования устаревших шифров, таких как DES или Blowfish, необходимо активировать устаревший провайдер в файле конфигурации openssl.cnf.

F.34.6.2. Обработка NULL значений #

Как и в стандартном SQL, все функции возвращают NULL, если хотя бы один из аргументов является NULL. Это может создавать уязвимости безопасности при небрежном использовании.

F.34.6.3. Ограничения безопасности #

Все функции pgcrypto выполняются внутри сервера базы данных. Это означает, что все данные и пароли передаются между pgcrypto и клиентскими приложениями в открытом виде. Поэтому необходимо:

  1. Подключайтесь локально или используйте SSL-соединения.

  2. Доверяйте и системному администратору, и администратору базы данных.

Если вы не можете, то лучше выполнять криптографию внутри клиентского приложения.

Реализация не устойчива к атакам через боковые каналы. Например, время, необходимое для завершения функции расшифровки pgcrypto, может варьироваться в зависимости от размера шифротекста.

F.34.7. Автор #

Марко Креен

pgcrypto использует код из следующих источников:

АлгоритмАвторИсточник происхождения
DES cryptDavid Burren и другиеFreeBSD libcrypt
MD5 криптографияPoul-Henning KampFreeBSD libcrypt
Blowfish cryptSolar Designerwww.openwall.com