F.38. pgcrypto — криптографические функции #
Модуль pgcrypto
предоставляет криптографические функции для Postgres Pro.
Данный модуль считается «доверенным», то есть его могут устанавливать обычные пользователи, имеющие право CREATE
в текущей базе данных.
Для работы pgcrypto
требуется OpenSSL — модуль не будет установлен, если Postgres Pro был собран без OpenSSL.
F.38.1. Стандартные функции хеширования #
F.38.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.38.1.2. hmac()
#
hmac(data text, key text, type text) returns bytea hmac(data bytea, key bytea, type text) returns bytea
Вычисляет имитовставку на основе хеша для данных data
с ключом key
. Параметр type
имеет то же значение, что и для digest()
.
Эта функция похожа на digest()
, но вычислить хеш с ней можно, только зная ключ. Это защищает от сценария подмены данных и хеша вместе с ними.
Если размер ключа больше размера блока хеша, он сначала хешируется, а затем используется в качестве ключа хеширования данных.
F.38.2. Функции хеширования пароля #
Функции crypt()
и gen_salt()
разработаны специально для хеширования паролей. Функция crypt()
выполняет хеширование, а gen_salt()
подготавливает параметры алгоритма для неё.
Алгоритмы в crypt()
отличаются от обычных алгоритмов хеширования MD5 и SHA1 в следующих аспектах:
Они медленные. Так как объём данных невелик, это единственный способ усложнить перебор паролей.
Они используют случайное значение, называемое солью, чтобы у пользователей с одинаковыми паролями зашифрованные пароли оказывались разными. Это также обеспечивает дополнительную защиту от получения обратного алгоритма.
Они включают в результат тип алгоритма, что допускает сосуществование паролей, хешированных разными алгоритмами.
Некоторые из них являются адаптируемыми — то есть с ростом производительности компьютеров эти алгоритмы можно настроить так, чтобы они стали медленнее, при этом сохраняя совместимость с существующими паролями.
В Таблице F.23 перечислены алгоритмы, поддерживаемые функцией crypt()
.
Таблица F.23. Алгоритмы, которые поддерживает crypt()
Алгоритм | Макс. длина пароля | Адаптивный? | Размер соли (бит) | Размер результата | Описание |
---|---|---|---|---|---|
bf | 72 | да | 128 | 60 | На базе Blowfish, вариация 2a |
md5 | без ограничений | нет | 48 | 34 | crypt на базе MD5 |
xdes | 8 | да | 24 | 20 | Расширенный DES |
des | 8 | нет | 12 | 13 | Изначальный crypt из UNIX |
F.38.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)) AS pswmatch FROM ... ;
Этот запрос возвращает true
, если введённый пароль правильный.
F.38.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.24.
Таблица F.24. Счётчики итераций для crypt()
Алгоритм | По умолчанию | Мин. | Макс. |
---|---|---|---|
xdes | 725 | 1 | 16777215 |
bf | 6 | 4 | 31 |
Для xdes
есть дополнительное ограничение: счётчик итераций должен быть нечётным.
При выборе подходящего числа итераций учтите, что оригинальный алгоритм DES crypt был рассчитан так, чтобы выдавать 4 хеша в секунду на компьютерах того времени. Если за секунду будет вычисляться меньше 4 хешей, скорее всего, возникнут определённые неудобства при пользовании. С другой стороны, скорость больше, чем 100 хешей в секунду, вероятно, будет слишком высокой.
В Таблице F.25 дана сводка относительной скорости различных алгоритмов хеширования. В таблице показано, сколько времени уйдёт на перебор всех комбинацией символов в восьмисимвольном пароле, в предположении, что пароль содержит только буквы в нижнем регистре, либо буквы в верхнем и нижнем регистре, а также цифры. В строках crypt-bf
числа после косой черты показывают значение параметра iter_count
функции gen_salt
.
Таблица F.25. Скорости алгоритмов хеширования
Алгоритм | Хешей/сек. | Для [a-z] | Для [A-Za-z0-9] | Длительность относительно md5 |
---|---|---|---|---|
crypt-bf/8 | 1792 | 4 года | 3927 лет | 100k |
crypt-bf/7 | 3648 | 2 года | 1929 лет | 50k |
crypt-bf/6 | 7168 | 1 год | 982 лет | 25k |
crypt-bf/5 | 13504 | 188 дней | 521 лет | 12.5k |
crypt-md5 | 171584 | 15 дней | 41 год | 1k |
crypt-des | 23221568 | 157.5 минут | 108 дней | 7 |
sha1 | 37774272 | 90 минут | 68 дней | 4 |
md5 (хеш) | 150085504 | 22.5 минут | 17 дней | 1 |
Замечания:
Для расчётов использовался процессор Intel Mobile Core i3.
Показатели алгоритмов
crypt-des
иcrypt-md5
взяты из вывода теста программы John the Ripper v1.6.38.Показатели
md5
получены программой 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.38.3. Функции шифрования на базе PGP #
Функции, описанные здесь, реализуют часть стандарта OpenPGP (RFC 4880), относящуюся к шифрованию. Они поддерживают шифрование как с симметричным, так и с закрытым ключом.
Зашифрованное PGP сообщение состоит из 2 частей или пакетов:
Пакет, содержащий сеансовый ключ — либо симметричный, либо открытый (в зашифрованном виде).
Пакет, содержащий данные, зашифрованные сеансовым ключом.
При шифровании с симметричным ключом (то есть, паролем):
Заданный пароль хешируется по алгоритму String2Key (S2K). Этот алгоритм подобен алгоритмам
crypt()
— специально замедлен и добавляет случайную соль — но на выход выдаёт двоичный ключ полной длины.Если требуется отдельный сеансовый ключ, генерируется новый случайный ключ. В противном случае в качестве сеансового будет использоваться непосредственно ключ S2K.
Когда используется непосредственно ключ S2K, в пакет сеансового ключа помещаются только параметры S2K. В противном случае сеансовый ключ шифруется ключом S2K и результат помещается в пакет сеансового ключа.
При шифровании с открытым ключом:
Генерируется новый случайный сеансовый ключ.
Он зашифровывается открытым ключом и помещается в пакет сеансового ключа.
В любом случае данные, которые должны быть зашифрованы, обрабатываются так:
Необязательная подготовка данных: сжатие, перекодировка в UTF-8 и/или преобразование концов строк.
Перед данными добавляется блок случайных байт. Это равносильно использованию случайного вектора инициализации.
В конце добавляется хеш SHA1 случайного префикса и данных.
Всё это шифруется сеансовым ключом и помещается в пакет данных.
F.38.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.38.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.38.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.38.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.38.3.5. pgp_key_id()
#
pgp_key_id(bytea) returns text
pgp_key_id
извлекает идентификатор ключа из открытого или закрытого ключа PGP. Она также может выдать идентификатор ключа, которым были зашифрованы данные, если ей передаётся зашифрованное сообщение.
Она может выдать два специальных идентификатора ключа:
SYMKEY
Сообщение зашифровано симметричным ключом.
ANYKEY
Сообщение зашифровано открытом ключом, но идентификатор ключа был удалён. Это означает, что вам надо будет перепробовать ключи, чтобы подобрать подходящий. Сама библиотека
pgcrypto
не генерирует такие сообщения.
Заметьте, что разные ключи могут иметь одинаковый идентификатор. Это редкое, но не невероятное явление. В таком случае клиентское приложение должно пытаться расшифровать данные с каждым ключом, пока не найдёт подходящий — примерно так же, как и с ANYKEY
.
F.38.3.6. armor()
, dearmor()
#
armor(data bytea [ , keys text[], values text[] ]) returns text dearmor(data text) returns bytea
Эти функции переводят двоичные данные в/из формата PGP «ASCII Armor», по сути представляющий собой кодировку Base64 с контрольными суммами и дополнительным форматированием.
Если задаются массивы keys
и values
, для каждой пары ключ/значения в формат Armor добавляется заголовок Armor. Оба массива должны быть одномерными и иметь одинаковую длину. Задаваемые ключи и значения могут содержать только символы ASCII.
F.38.3.7. pgp_armor_headers
#
pgp_armor_headers(data text, key out text, value out text) returns setof record
Функция pgp_armor_headers()
извлекает заголовки Armor из параметра data
. Она возвращает набор строк с двумя столбцами, key и value. Если в ключах или значениях оказываются символы не ASCII, они воспринимаются как UTF-8.
F.38.3.8. Параметры функций PGP #
Имена параметров подобны принятым в GnuPG. Значение параметра должно задаваться после знака равно; друг от друга параметры отделяются запятыми. Например:
pgp_sym_encrypt(data, psw, 'compress-algo=1, cipher-algo=aes256')
Все эти параметры, кроме convert-crlf
, применяются только к функциям шифрования. Функции расшифровывания получают параметры из данных PGP.
Вероятно, самые интересные параметры — это compress-algo
и unicode-mode
. Остальные должны иметь достаточно адекватные значения по умолчанию.
F.38.3.8.1. cipher-algo #
Выбирает алгоритм шифрования.
Значения: bf, aes128, aes192, aes256, 3des, cast5
По умолчанию: aes128
Применим к: pgp_sym_encrypt, pgp_pub_encrypt
F.38.3.8.2. compress-algo #
Выбирает алгоритм сжатия. Принимается, только если Postgres Pro собран с zlib.
Значения:
0 — без сжатия
1 — сжатие ZIP
2 — сжатие ZLIB (= ZIP плюс метаданные и CRC блоков)
По умолчанию: 0
Применим к: pgp_sym_encrypt, pgp_pub_encrypt
F.38.3.8.3. compress-level #
Определяет уровень сжатия. Чем больше уровень, тем меньшего объёма результат, но длительнее процесс. Значение 0 отключает сжатие.
Значения: 0, 1-9
По умолчанию: 6
Применим к: pgp_sym_encrypt, pgp_pub_encrypt
F.38.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.38.3.8.5. disable-mdc #
Не защищать данные хешем SHA-1. Единственная разумная причина использовать этот параметр — добиться совместимости с древними программами PGP, вышедшими до того, как в RFC 4880 была предусмотрена защита пакетов с SHA-1. Все последние реализации с gnupg.org и pgp.com прекрасно поддерживают это.
Значения: 0, 1
По умолчанию: 0
Применим к: pgp_sym_encrypt, pgp_pub_encrypt
F.38.3.8.6. sess-key #
Использовать отдельный сеансовый ключ. Для шифрования с открытым ключом всегда используется отдельный сеансовый ключ; этот параметр предназначен для шифрования с симметричным ключом, которое по умолчанию использует непосредственно ключ S2K.
Значения: 0, 1
По умолчанию: 0
Применим к: pgp_sym_encrypt
F.38.3.8.7. s2k-mode #
Режим алгоритма S2K.
Значения:
0 — Без соли. Опасно!
1 — С солью, но с фиксированным числом итераций.
3 — С переменным числом итераций.
По умолчанию: 3
Применим к: pgp_sym_encrypt
F.38.3.8.8. s2k-count #
Число итераций для алгоритма S2K. Оно должно быть не меньше 1024 и не больше 65011712.
По умолчанию: случайное значение между 65536 и 253952
Применим к: pgp_sym_encrypt, только с s2k-mode=3
F.38.3.8.9. s2k-digest-algo #
Выбирает алгоритм хеширования, который будет использоваться для вычисления S2K.
Значения: md5, sha1
По умолчанию: sha1
Применим к: pgp_sym_encrypt
F.38.3.8.10. s2k-cipher-algo #
Выбирает шифр, который будет использоваться для шифрования отдельного сеансового ключа.
Значения: bf, aes, aes128, aes192, aes256
По умолчанию: используется cipher-algo
Применим к: pgp_sym_encrypt
F.38.3.8.11. unicode-mode #
Определяет, преобразовывать ли текстовые данные из внутренней кодировки базы данных в UTF-8 и обратно. Если кодировка базы уже UTF-8, перекодировка не производится, но сообщение помечается как UTF-8. Без данного параметра этого не происходит.
Значения: 0, 1
По умолчанию: 0
Применим к: pgp_sym_encrypt, pgp_pub_encrypt
F.38.3.9. Формирование ключей PGP с применением GnuPG #
Формирование нового ключа:
gpg --gen-key
Предпочитаемый тип ключей: «DSA and Elgamal».
Для шифрования RSA вы должны создать главный ключ либо DSA, либо 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
Прежде чем передавать эти ключи функциям PGP, вы должны применить функцию dearmor()
к этим ключам. Либо, если вы можете обработать двоичные данные, уберите -a
из команды.
Дополнительную информацию вы можете получить в руководстве man gpg
, The GNU Privacy Handbook (Руководство GNU по обеспечению конфиденциальности) и другой документации на сайте https://www.gnupg.org/.
F.38.3.10. Ограничения кода PGP #
Не поддерживается подписывание. Это также означает, что принадлежность подключа шифрования главному ключу не проверяется.
Не поддерживается использование ключа шифрования в качестве главного ключа. Так как подобная практика обычно не приветствуется, это не должно быть проблемой.
Нет поддержки нескольких подключей. Это может представляться проблемой, так как такие ключи не редкость. С другой стороны, вы всё равно не должны использовать обычные ключи GPG/PGP с
pgcrypto
, а должны создать новые, учитывая, что это другой сценарий использования.
F.38.4. Низкоуровневые функции шифрования #
Эти функции выполняют только шифрование данных; они не предоставляют расширенные возможности шифрования PGP. Таким образом, с ними связаны следующие проблемы:
Они используют ключ пользователя непосредственно в качестве ключа шифрования.
Они не обеспечивают проверку целостности, которая должна выявлять модификацию зашифрованных данных.
Они рассчитаны на то, что пользователи будут управлять всеми параметрами шифрования самостоятельно, даже вектором инициализации.
Они не рассчитаны на текст.
Поэтому с появлением поддержки шифрования 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
имеет следующий формат:
алгоритм
[-
режим
] [/pad:
дозаполнение
]
где допустимый алгоритм
:
bf
— Blowfishaes
— AES (Rijndael-128, -192 или -256)
допустимый режим
:
cbc
— следующий блок зависит от предыдущего (по умолчанию)ecb
— каждый блок шифруется отдельно (только для тестирования)
и допустимое дозаполнение
:
pkcs
— данные могут быть любой длины (по умолчанию)none
— размер данных должен быть кратен размеру блока шифра
Так что, например, эти вызовы равнозначны:
encrypt(data, 'fooz', 'bf') encrypt(data, 'fooz', 'bf-cbc/pad:pkcs')
Для функций encrypt_iv
и decrypt_iv
параметр iv
задаёт начальное значение для режима CBC; для ECB он игнорируется. Оно обрезается или дополняется нулями, если его размер не равен ровно размеру блока. В функциях без этого параметра оно по умолчанию заполняется нулями.
F.38.5. Функции получения случайных данных #
gen_random_bytes(count integer) returns bytea
Возвращает криптографически стойкие случайные байты в количестве count
. За один вызов можно получить максимум 1024 байт. Это ограничение предотвращает исчерпание пула энтропии.
gen_random_uuid() returns uuid
Возвращает UUID версии 4 (случайный). (Эта функция в данном модуле считается устаревшей, она вызывает одноимённую функцию ядра.)
F.38.6. Примечания #
F.38.6.1. Конфигурирование #
Модуль pgcrypto
настраивается согласно установкам, полученным в главном скрипте configure
Postgres Pro. На его конфигурацию влияют аргументы --with-zlib
и --with-ssl=openssl
.
При компиляции с zlib шифрующие функции PGP могут сжимать данные перед шифрованием.
Для работы pgcrypto
требуется OpenSSL. Без этой библиотеки модуль не будет собран и установлен.
Чтобы использовать устаревшие алгоритмы шифрования, такие как DES или Blowfish, при компиляции с OpenSSL версии 3.0.0 и выше, нужно активировать поставщиков этих алгоритмов в файле конфигурации openssl.cnf
.
F.38.6.2. Обработка NULL #
Как и положено по стандарту SQL, все эти функции возвращают NULL, если один из аргументов — NULL. Это может угрожать безопасности при неаккуратном использовании.
F.38.6.3. Ограничения безопасности #
Все функции pgcrypto
выполняются внутри сервера баз данных. Это означает, что все данные и пароли передаются между функциями pgcrypto
и клиентскими приложениями открытым текстом. Поэтому вы должны:
Подключаться локально или использовать подключения SSL.
Доверять и системе, и администратору баз данных.
Если это невозможно, лучше произвести шифрование в клиентском приложении.
Эта реализация не противостоит атакам по сторонним каналам. Например, время, требующееся для выполнения функции дешифрования pgcrypto
, будет разным для разного шифротекста заданного размера.
F.38.7. Автор #
Марко Крин <markokr@gmail.com>
Модуль pgcrypto
заимствует код из следующих источников:
Алгоритм | Автор | Источник исходного кода |
---|---|---|
Шифрование DES | Дэвид Буррен и другие | FreeBSD, libcrypt |
Хеширование MD5 | Пол-Хеннинг Камп | FreeBSD, libcrypt |
Шифрование Blowfish | Solar Designer | www.openwall.com |