Эта статья может быть слишком технической для понимания большинством читателей . Март 2017 г. ) ( Узнайте, как и когда удалить этот шаблон сообщения ) ( |
Общий | |
---|---|
Дизайнеров | Нильс Прово , Давид Мазьер |
Впервые опубликовано | 1999 г. |
Полученный из | Blowfish (шифр) |
Деталь | |
Размеры дайджеста | 184 бит |
Раундов | переменная через параметр стоимости |
bcrypt - это функция хеширования паролей, разработанная Нильсом Провосом и Дэвидом Мазьером на основе шифра Blowfish и представленная на USENIX в 1999 году. [1] Помимо включения соли для защиты от атак радужной таблицы , bcrypt является адаптивной функцией: с течением времени, количество итераций может быть увеличено, чтобы сделать его медленнее, поэтому он остается устойчивым к атакам методом перебора даже при увеличении вычислительной мощности.
Функция bcrypt является алгоритмом хеширования пароля по умолчанию для OpenBSD [2] и использовалась по умолчанию для некоторых дистрибутивов Linux, таких как SUSE Linux . [3]
Существуют реализации bcrypt для C , C ++ , C # , Elixir , [4] Go , [5] Java , [6] [7] JavaScript , [8] Perl , PHP , Python , [9] Ruby и других языков.
Фон [ править ]
Blowfish выделяется среди блочных шифров своим дорогостоящим этапом настройки ключа. Он начинается с подключей в стандартном состоянии, затем использует это состояние для выполнения блочного шифрования с использованием части ключа и использует результат этого шифрования (который более точен при хешировании) для замены некоторых подключей. Затем он использует это измененное состояние для шифрования другой части ключа и использует результат для замены большего количества подключей. Он действует таким образом, используя прогрессивно изменяемое состояние для хеширования ключа и замены битов состояния, пока не будут установлены все подключи.
Прово и Мазьер воспользовались этим и пошли дальше. Они разработали новый алгоритм настройки ключей для Blowfish, назвав получившийся шифр «Eksblowfish» («дорогостоящее расписание ключей для Blowfish»). Настройка ключа начинается с измененной формы стандартной настройки ключа Blowfish, в которой и соль, и пароль используются для установки всех подключей. Затем существует ряд раундов, в которых применяется стандартный алгоритм набора ключей Blowfish с альтернативным использованием соли и пароля в качестве ключа, причем каждый раунд начинается с состояния подключей из предыдущего раунда. Теоретически это не сильнее стандартного расписания ключей Blowfish, но количество раундов смены ключей можно настроить; поэтому этот процесс можно сделать сколь угодно медленным, что помогает предотвратить атаки методом перебора хэша или соли.
Описание [ править ]
Хеш-строка bcrypt имеет вид:
$ 2b $ [стоимость] $ [соль из 22 символов] [хэш из 31 символа]
Например:
$ 2a $ 10 $ N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy\ __ / \ / \ ____________________ / \ _____________________________ / Alg Cost Salt Hash
Где:
$2a$
: Идентификатор алгоритма хеширования (bcrypt)10
: Фактор стоимости (210
==> 1024 раунда)N9qo8uLOickgx2ZMRZoMye
: 16-байтовая (128-битная) соль, закодированная в base64 до 22 символовIjZAgcfl7p92ldGxad68LJZdL17lhWy
: 24-байтовый (192-битный) хэш, закодированный в base64 до 31 символа
Префикс «$ 2a $» или «$ 2b $» (или «$ 2y $») в строке хеша в файле теневого пароля указывает, что строка хеша является хешем bcrypt в модульном формате шифрования. [10] Остальная часть хеш-строки включает параметр стоимости, 128-битную соль ( Radix-64, закодированную как 22 символа) и 192 бита результирующего хеш-значения ( Radix-64 закодировано как 31 символ). [11] Кодировка Radix-64 использует алфавит unix / crypt и не является «стандартной» Base-64 . [12] [13] Параметр стоимости определяет количество итераций раскрытия ключа как степень двойки, которая является входом для алгоритма шифрования.
Например, запись теневых паролей $2a$10$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy
определяет параметр стоимости 10, что указывает на 2 10 ключевых раундов расширения. Соль есть, N9qo8uLOickgx2ZMRZoMye
а полученный хеш - это IjZAgcfl7p92ldGxad68LJZdL17lhWy
. Согласно стандартной практике, сам пароль пользователя не сохраняется.
В большинстве языков программирования можно проверить, представляет ли текст закодированный пароль bcrypt, используя регулярное выражение . Эту функцию можно использовать для проверки правильности кодировки пароля. Ниже приведен пример в Python в виде регулярного выражения , который работает на большинстве языков программирования :
импорт ре# Упрощенная версия регулярного выражения для соответствия паролям bcryptregular_expression = "^ [$] 2 [abxy]? [$] (?: 0 [4-9] | [12] [0-9] | 3 [01]]" [$] [./ 0-9a-zA -Z] {53} $ "# Пароль bcrypt, закодированный с версией "2y" и стоимостью 12encoded_password = "$ 2y $ 12 $ PEmxrth.vjPDazPWQcLs6u9GRFLJvneUkcf / vcXn8L.bzaBUKeX4W"match = re . поиск ( регулярное_выражение , закодированный_пароль )если совпадает : print ( «ДА! У нас есть совпадение!» )еще : print ( "Нет совпадений" )
При выполнении эта программа дает следующий результат:
ДА! У нас есть матч!
Помимо определения того, является ли данный текст допустимым паролем bcrypt, регулярное выражение может использоваться для извлечения из него информации, такой как его версия, стоимость, соль и хэш. Ниже приведен пример более сложного регулярного выражения на языке Python, которое извлекает информацию из закодированного пароля. Его можно настроить для работы на большинстве языков программирования :
# Для этого примера требуется python 3.6+импорт ре# Расширенная версия регулярного выражения для сопоставления паролей bcrypt и извлечения информации о нихрегулярное_выражение = "^ [$] (? P <версия> 2 [abxy]?) [$] (? P <сила> (? P <стоимость> (0 [4-9] | [12] [0-9]] | 3 [01]))) [$] (? P <пароль> ((? P <salt> [./0-9a-zA-Z] {22} ) (? P <hash> [./0- 9a-zA-Z] {31} ))) $ "# Ниже то же регулярное выражение для использования на других языках# Обратите внимание, что буква "P" была удалена из имен## ^ [$] (? <версия> 2 [abxy]?) [$] (? <сила> (? <стоимость> (0 [4-9] | [12] [0-9] | 3 [01] ))) [$] (? <password> ((? <salt> [./0-9a-zA-Zpting{22}) (? <hash> [./0-9a-zA-Zpting{31 }))) $шаблон = re . компилировать ( регулярное_выражение )# Пароль bcrypt, закодированный с версией "2y" и стоимостью 12encoded_password = "$ 2y $ 12 $ PEmxrth.vjPDazPWQcLs6u9GRFLJvneUkcf / vcXn8L.bzaBUKeX4W"совпадение = шаблон . совпадение ( encoded_password )если совпадение : print ( «ДА! У нас есть совпадение!» ) печать ( "" ) print ( f "Версия: { match . group ( 'version' ) } " ) print ( f "Стоимость: { match . group ( 'cost' ) } " ) print ( f "Сила (другое название стоимости): { match . group ( ' Strength ' ) } " ) print ( f "Пароль: { match . group ( 'password' ) } " ) print ( f "Соль: { матч . группа ( 'соль' ) } " ) print ( f "Hash: { match . group ( 'hash' ) } " )еще : print ( "Нет совпадений" )
При выполнении эта программа дает следующий результат:
ДА! У нас есть матч!Версия: 2yСтоимость: 12Сила (другое название стоимости): 12Пароль: PEmxrth.vjPDazPWQcLs6u9GRFLJvneUkcf / vcXn8L.bzaBUKeX4WСоль: PEmxrth.vjPDazPWQcLs6uХеш: 9GRFLJvneUkcf / vcXn8L.bzaBUKeX4W
История версий [ править ]
2 доллара (1999)
Исходная спецификация bcrypt определяла префикс $2$
. Это соответствует формату Modular Crypt Format [14] , используемому при хранении паролей в файле паролей OpenBSD:
$1$
: Крипта на основе MD5 ('md5crypt')$2$
: Крипта на основе Blowfish ('bcrypt')$sha1$
: Крипта на основе SHA-1 ('sha1crypt')$5$
: Крипта на основе SHA-256 ('sha256crypt')$6$
: Крипта на основе SHA-512 ('sha512crypt')
2 доллара США
Исходная спецификация не определяла, как обрабатывать символы, отличные от ASCII, и как обрабатывать нулевой терминатор. Спецификация была изменена, чтобы указать, что при хешировании строк:
- строка должна быть в кодировке UTF-8
- нуль-терминатор должен быть включен
С этим изменением версия была изменена на $2a$
[15]
$ 2x $, $ 2y $ (июнь 2011 г.)
В июне 2011 года была обнаружена ошибка в crypt_blowfish , PHP-реализации bcrypt. Это было неправильное обращение с символами с 8-битным набором. [16] Они предложили системным администраторам обновить существующую базу паролей, заменив $2a$
на $2x$
, чтобы указать, что эти хэши плохие (и необходимо использовать старый сломанный алгоритм). Они также предложили идею иметь crypt_blowfish Emit $2y$
для хешей , порожденных фиксированным алгоритмом.
Никто другой, включая канонический OpenBSD, не принял идею 2x / 2y. Это изменение маркера версии было ограничено crypt_blowfish .
2 миллиарда долларов (февраль 2014 г.)
Ошибка была обнаружена в реализации bcrypt в OpenBSD. Они сохраняли длину своих строк в беззнаковом символе ( т.е. 8-битном Byte
). [15] Если пароль был длиннее 255 символов, он переполнялся и переносился на 255. [17]
bcrypt был создан для OpenBSD. Когда в их библиотеке обнаружилась ошибка, они решили увеличить номер версии.
Алгоритм [ править ]
Алгоритм bcrypt является результатом 64-кратного шифрования текста OrpheanBeholderScryDoubt с помощью Blowfish . В bcrypt обычная функция настройки клавиш Blowfish заменена дорогостоящей функцией настройки клавиш (EksBlowfishSetup):
Функция bcrypt Ввод: стоимость: Число (4..31) log 2 (Итерации). например, 12 ==> 2 12 = 4096 итераций соль: массив байтов (16 байтов) случайная соль пароль: массив байтов (1..72 байта) пароль в кодировке UTF-8 Вывод: хэш: массив байтов (24 байта) // Инициализировать состояние Blowfish с помощью дорогостоящего алгоритма настройки ключей // P: массив из 18 подключей (UInt32 [18]) // S: четыре блока подстановки (S-блоки), S 0 ... S 3 . Каждый S-блок имеет размер 1024 байта (UInt32 [256]) P , S ← EksBlowfishSetup ( стоимость , соль , пароль ) // Повторное шифрование текста "OrpheanBeholderScryDoubt" 64 раза ctext ← "OrpheanBeholderScryDoubt" // 24 байта ==> три 64-битных блока repeat (64) ctext ← EncryptECB ( P , S , ctext ) // шифрование с использованием стандартного Blowfish в ECB Режим // 24-байтовый ctext получается хеш пароля return Concatenate ( cost , salt , ctext )
Дорогая настройка ключа [ править ]
Алгоритм bcrypt сильно зависит от алгоритма настройки ключа Eksblowfish, который работает следующим образом:
Функция EksBlowfishSetup Вход: пароль: массив байтов (1..72 байта) Соль пароля в кодировке UTF-8 : массив байтов (16 байтов) случайная стоимость соли : Число (4..31) log 2 (Итерации). например, 12 ==> 2 12 = 4 096 итераций Вывод: P: массив UInt32, состоящий из 18 подключей на раунд S 1 ..S 4 : массив UInt32, массив из четырех SBox; каждый SBox имеет размер 256 UInt32 ( т.е. 1024 КБ) // Инициализируем P (подключи) и S (поля подстановки) шестнадцатеричными цифрами pi P , S ← InitialState () // Перестановка P и S на основе пароля и соли P , S ← ExpandKey ( P , S , соль , пароль ) // Это «дорогая» часть «дорогостоящей настройки ключа». // В остальном настройка клавиш идентична Blowfish. повторить (2 стоимости ) P , S ← ExpandKey ( P , S , 0, пароль) P , S ← ExpandKey ( P , S , 0, соль) вернуть P , S
InitialState работает так же, как в исходном алгоритме Blowfish, заполняя записи P-массива и S-блока дробной частью в шестнадцатеричном формате.
Развернуть ключ [ изменить ]
Функция ExpandKey выполняет следующие действия:
Функция ExpandKey Ввод: пароль: массив байтов (1..72 байта) Соль пароля в кодировке UTF-8 : байт [16] случайная соль P: массив UInt32 Массив из 18 подключей S 1 ..S 4 : UInt32 [1024] Четыре SBoxes 1 КБ Вывод: P: массив UInt32 Массив из 18 подключей на раунд S 1 ..S 4 : UInt32 [1024] Четыре SBox по 1 КБ // Смешайте пароль в массиве подключей P для n ← от 1 до 18 do P n ← P n xor password [32 (n-1) .. 32n-1] // обрабатываем пароль как циклический // Рассматривайте 128-битную соль как две 64-битные половинки (размер блока Blowfish). saltHalf [0] ← salt [0..63] // Нижние 64 бита соли saltHalf [1] ← salt [64..127] // 64 верхних бита соли // Инициализируем 8-байтовый (64-битный) буфер всеми нулями. блок ← 0 // Смешиваем внутреннее состояние в P-блоки для n ← от 1 до 9 do // xor 64-битный блок с 64-битным полублоком соли ← block xor saltHalf [(n-1) mod 2] // каждая итерация чередуется между saltHalf [0] и saltHalf [1] // зашифровать блок с использованием текущего ключа блока расписания ← Шифровать ( P , S , блок ) P 2n ← block [0..31] // младшие 32 бита блока P 2n + 1 ← block [32..63] // старшие 32 бита блока // Смешиваем зашифрованное состояние с внутренними S-блоками состояния для i ← от 1 до 4 do для n ← от 0 до 127 do block ← Encrypt ( state , block xor salt [64 (n-1) .. 64n-1]) // как указано выше S i [2n] ← block [0..31] // младшие 32 бита S i [2n + 1] ← block [32..63] // старшие 32 бита возвращают состояние
Следовательно, это то же самое, что и обычное расписание ключей Blowfish, поскольку все операции XOR с нулевым значением соли неэффективны. аналогичен, но использует соль как 128-битный ключ.ExpandKey(state, 0, key)
ExpandKey(state, 0, salt)
Пользовательский ввод [ править ]
Многие реализации bcrypt усекают пароль до первых 72 байтов, следуя реализации OpenBSD.
Сам математический алгоритм требует инициализации 18 32-битными подключами (что эквивалентно 72 октетам / байтам). Исходная спецификация bcrypt не требует какого-либо конкретного метода для преобразования текстовых паролей из пользовательского пространства в числовые значения для алгоритма. Один краткий комментарий в тексте упоминает, но не требует, возможность простого использования значения в кодировке ASCII символьной строки: «Наконец, аргумент ключа - это секретный ключ шифрования, который может быть выбранным пользователем паролем до 56 байтов (включая завершающий нулевой байт, когда ключ является строкой ASCII) ". [1]
Обратите внимание, что в приведенной выше цитате упоминаются пароли «до 56 байт», хотя сам алгоритм использует начальное значение в 72 байта. Хотя Прово и Мазьер не указывают причину более короткого ограничения, они могли быть мотивированы следующим утверждением из оригинальной спецификации Брюса Шнайера для Blowfish: «Ограничение размера ключа в 448 [бит] гарантирует, что [ sic ] каждый бит каждого подключа зависит от каждого бита ключа ". [18]
Реализации различались в своем подходе к преобразованию паролей в исходные числовые значения, включая иногда снижение надежности паролей, содержащих символы, отличные от ASCII. [19]
Критика [ править ]
Максимальная длина пароля [ править ]
bcrypt имеет максимальную длину пароля 72 байта. Этот максимум исходит от первой операции функции ExpandKey , xor's
в которой 18 4-байтовых подключей (P) с паролем:
P 1 ..P 18 ← P 1 ..P 18 xor passwordBytes
Пароль (который закодирован в UTF-8) повторяется до тех пор, пока он не достигнет 72 байтов. Например, пароль:
correct horse battery staple␀
(29 байт)
Повторяется до тех пор, пока он не совпадет с 72 байтами из 18 P на раунд подключей:
correct horse battery staple␀correct horse battery staple␀correct horse
(72 байта)
Это также означает, что если пароль был длиннее 72 байтов в кодировке UTF-8: пароль был бы усечен. Для некоторых символов может потребоваться 4 байта при кодировке UTF-8. Это означает, что в худшем случае это может ограничить пароль до 18 символов:
𐑜𐑝𐑟𐑥𐑷𐑻𐑽𐑾𐑿𐑿𐑰𐑩𐑛𐑙𐑘𐑙𐑒𐑔
(18 символов, 72 байта)
Этот предел 72-байтовых паролей (18 подключей по 4 байта каждый) можно было устранить несколькими способами:
Решение 1. Увеличьте количество подключей [ править ]
Вы можете увеличить количество используемых подключей P и, следовательно, увеличить доступную максимальную длину пароля. Брюс Шнайер, первоначальный автор Blowfish, заметил, что люди могут играть, добавляя больше раундов, [20] что требует большего количества подключей, что для bcrypt увеличит максимальную длину пароля. Обратной стороной этого является то, что по-прежнему существует максимальная длина пароля, а не максимальная длина.
Решение 2. Продолжайте x или смешивайте ключевые байты с массивом подключей P [ править ]
Смешивание паролей продолжается только до тех пор, пока P 1 ... P 18 . Как только вы достигли P 18, а у вас остались необработанные байты пароля, продолжите процесс xor на P 1 . Обратной стороной этого подхода является то, что время вычисления приводит к утечке длины пароля (утечка информации по побочному каналу).
Решение 3. Предварительно присвоенный пароль [ править ]
Длинный пароль можно предварительно зашифровать с помощью другой криптографической функции хеширования. Эта функция всегда выводит дайджест фиксированного размера. Пока результирующий дайджест составляет менее 72 байтов: он может быть передан в bcrypt без обрезания. Это подход, используемый Dropbox [21] и другими. Например, рассмотрим пароль:
ਤੇਜ਼ ਭੂਰੇ ਲੂੰਬੜ ਨੇ ਆਲਸੀ ਕੁੱਤੇ ਉੱਤੇ ਛਾਲ ਮਾਰ ਦਿੱਤੀ
(32 символа, 126 байт)
Если вы хэшируете этот пароль с помощью SHA-256, вы получите 256-битный (32-байтовый) дайджест:
A7 B2 AA 86 CB 57 D3 08 EA 54 9F 34 E5 91 7E 8C 06 9B D0 0D 34 28 B1 CA 8D 71 B2 2E 84 DA C0 F8
Кодировка дайджеста Base64 дает строку, состоящую из 44 символов:
p7KqhstX0wjqVJ805ZF+jAab0A00KLHKjXGyLoTawPg=
(44 символа, 44 байта)
и всегда будет соответствовать 72-байтовому пределу bcrypt.
Это похоже на подход, используемый Pufferfish2 , эволюцией bcrypt с жестким кешированием, который использует HMAC SHA-512 для преобразования пароля в 64-байтовый (512-битный) ключ фиксированной длины.
Усечение хэша пароля [ править ]
Алгоритм bcrypt включает многократное шифрование 24-байтового текста:
OrpheanBeholderScryDoubt
(24 байта)
Это генерирует 24-байтовый зашифрованный текст, например:
85 20 af 9f 03 3d b3 8c 08 5f d2 5e 2d aa 5e 84 a2 b9 61 d2 f1 29 c9 a4
(24 байта)
Который затем должен получить кодировку radix-64 до 32 символов:
hSCvnwM9s4wIX9JeLapehKK5YdLxKcmk
(32 символа)
Но каноническая реализация OpenBSD усекает хэш пароля до 23 байтов:
85 20 af 9f 03 3d b3 8c 08 5f d2 5e 2d aa 5e 84 a2 b9 61 d2 f1 29 c9
a4(23 байта)
и результирующий текст в кодировке base-64, который обычно будет:
hSCvnwM9s4wIX9JeLapehKK5YdLxKck =
имеет завершающий = удален, оставляя хеш:
hSCvnwM9s4wIX9JeLapehKK5YdLxKck
Непонятно, почему каноническая реализация удаляет 8 бит из полученного хэша пароля.
Использование нестандартной кодировки base64 [ править ]
Base64 кодирования , используемый канонический OpenBSD используется реализация словаря кодирования , который отличается от тех , доступны почти в каждом языке и платформе. Кодировка совместима с crypt . [22] Это означает, что кодировка несовместима с RFC 4648 .
См. Также [ править ]
- bcrypt - это также название межплатформенной утилиты для шифрования файлов, реализующей Blowfish, разработанной в 2002 году. [23] [24] [25] [26]
- Argon2 (алгоритм, выбранный на Конкурсе хеширования паролей в 2015 году)
- Crypt (C) # Схема на основе Blowfish crypt - схема хранения и проверки паролей - Blowfish
- Ключевое растяжение
- PBKDF2 (функция вывода ключей на основе пароля 2)
- зашифровать
- PufferFish - это функция хеширования паролей с жестким кешем, основанная на улучшенной конструкции bcrypt.
Ссылки [ править ]
- ^ а б Провос, Нильс; Мазьер, Давид; Талан Джейсон Саттон 2012 (1999). «Схема паролей, адаптируемая к будущему» . Труды Ежегодной технической конференции USENIX 1999 : 81–92.
- ^ «Передача первой работы в репо» . 13 февраля 1997 г.
- ^ «Объявление безопасности SUSE: (SUSE-SA: 2011: 035)» . 23 августа 2011 года Архивировано из оригинала 4 марта 2016 года . Проверено 20 августа 2015 года .
Реализация crypt () в SUSE поддерживает функцию хеширования паролей blowfish (id $ 2a), и вход в систему по умолчанию также использует этот метод.
- ^ Уитлок, Дэвид. «Bcrypt Elixir: алгоритм хеширования паролей bcrypt для Elixir» . GitHub . Riverrun.
- ^ "Пакет bcrypt" . godoc.org .
- ^ «jBCrypt - надежное хеширование паролей для Java» . www.mindrot.org . Проверено 11 марта 2017 .
- ^ "bcrypt - автономная реализация Java хеш-функции паролей bcrypt" . github.com . Проверено 19 июля 2018 .
- ^ "bcryptjs" . npm .
- ^ Стаффт, Дональд. «bcrypt: Современное хеширование паролей для вашего программного обеспечения и ваших серверов» - через PyPI.
- ^ passlib. «Модульный формат склепа» .
- ^ passlib. "bcrypt" .
- ^ «Современное (-ish) хеширование паролей для вашего программного обеспечения и ваших серверов: pyca / bcrypt» . 18 ноября 2019 г. - через GitHub.
- ^ "GitHub - bcgit / bc-java: Распространение Java Bouncy Castle (зеркало)" . 18 ноября 2019 г. - через GitHub.
- ^ «Модульный формат крипт - документация Passlib v1.7.1» . passlib.readthedocs.io .
- ^ a b «Исправлены ошибки хеширования паролей bcrypt, изменения версии и последствия» . undeadly.org .
- ^ Дизайнер, Solar. "oss-sec: запрос CVE: неверная обработка 8-битных символов crypt_blowfish" . seclists.org .
- ^ " ' изменения версии bcrypt' - MARC" . marc.info .
- ^ Шнайер, Брюс (1994). «Быстрое программное шифрование, описание нового ключа переменной длины, 64-битный блочный шифр (Blowfish)» . Труды Кембриджского семинара по безопасности (декабрь 1993 г.) . Springer-Verlag: 191–204.
- ^ "Рекомендации по безопасности jBCrypt" . 1 февраля 2010 г.И «Изменения в CRYPT_BLOWFISH в PHP 5.3.7» . php.net .
- ^ https://www.schneier.com/academic/blowfish/
- ^ https://dropbox.tech/security/how-dropbox-securely-stores-your-passwords
- ^ https://medium.com/hackernoon/the-bcrypt-protocol-is-kind-of-a-mess-4aace5eb31bd
- ^ http://bcrypt.sourceforge.net Домашняя страница программы шифрования файлов bcrypt
- ^ "bcrypt APK для Android - скачать бесплатно на Droid Informer" . droidinformer.org .
- ^ "Пакет T2 - ствол - bcrypt - Утилита для шифрования файлов" . t2sde.org .
- ^ "Oracle GoldenGate の ラ イ セ ン ス" . docs.oracle.com .
Внешние ссылки [ править ]
- crypt_blowfish, реализация, поддерживаемая Openwall