Из Википедии, бесплатной энциклопедии
Перейти к навигации Перейти к поиску

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: Фактор стоимости (2 10==> 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 2nblock [0..31] // младшие 32 бита блока P 2n + 1block [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.

Ссылки [ править ]

  1. ^ а б Провос, Нильс; Мазьер, Давид; Талан Джейсон Саттон 2012 (1999). «Схема паролей, адаптируемая к будущему» . Труды Ежегодной технической конференции USENIX 1999 : 81–92.
  2. ^ «Передача первой работы в репо» . 13 февраля 1997 г.
  3. ^ «Объявление безопасности SUSE: (SUSE-SA: 2011: 035)» . 23 августа 2011 года Архивировано из оригинала 4 марта 2016 года . Проверено 20 августа 2015 года . Реализация crypt () в SUSE поддерживает функцию хеширования паролей blowfish (id $ 2a), и вход в систему по умолчанию также использует этот метод.
  4. ^ Уитлок, Дэвид. «Bcrypt Elixir: алгоритм хеширования паролей bcrypt для Elixir» . GitHub . Riverrun.
  5. ^ "Пакет bcrypt" . godoc.org .
  6. ^ «jBCrypt - надежное хеширование паролей для Java» . www.mindrot.org . Проверено 11 марта 2017 .
  7. ^ "bcrypt - автономная реализация Java хеш-функции паролей bcrypt" . github.com . Проверено 19 июля 2018 .
  8. ^ "bcryptjs" . npm .
  9. ^ Стаффт, Дональд. «bcrypt: Современное хеширование паролей для вашего программного обеспечения и ваших серверов» - через PyPI.
  10. ^ passlib. «Модульный формат склепа» .
  11. ^ passlib. "bcrypt" .
  12. ^ «Современное (-ish) хеширование паролей для вашего программного обеспечения и ваших серверов: pyca / bcrypt» . 18 ноября 2019 г. - через GitHub.
  13. ^ "GitHub - bcgit / bc-java: Распространение Java Bouncy Castle (зеркало)" . 18 ноября 2019 г. - через GitHub.
  14. ^ «Модульный формат крипт - документация Passlib v1.7.1» . passlib.readthedocs.io .
  15. ^ a b «Исправлены ошибки хеширования паролей bcrypt, изменения версии и последствия» . undeadly.org .
  16. ^ Дизайнер, Solar. "oss-sec: запрос CVE: неверная обработка 8-битных символов crypt_blowfish" . seclists.org .
  17. ^ " ' изменения версии bcrypt' - MARC" . marc.info .
  18. ^ Шнайер, Брюс (1994). «Быстрое программное шифрование, описание нового ключа переменной длины, 64-битный блочный шифр (Blowfish)» . Труды Кембриджского семинара по безопасности (декабрь 1993 г.) . Springer-Verlag: 191–204.
  19. ^ "Рекомендации по безопасности jBCrypt" . 1 февраля 2010 г.И «Изменения в CRYPT_BLOWFISH в PHP 5.3.7» . php.net .
  20. ^ https://www.schneier.com/academic/blowfish/
  21. ^ https://dropbox.tech/security/how-dropbox-securely-stores-your-passwords
  22. ^ https://medium.com/hackernoon/the-bcrypt-protocol-is-kind-of-a-mess-4aace5eb31bd
  23. ^ http://bcrypt.sourceforge.net Домашняя страница программы шифрования файлов bcrypt
  24. ^ "bcrypt APK для Android - скачать бесплатно на Droid Informer" . droidinformer.org .
  25. ^ "Пакет T2 - ствол - bcrypt - Утилита для шифрования файлов" . t2sde.org .
  26. ^ "Oracle GoldenGate の ラ イ セ ン ス" . docs.oracle.com .

Внешние ссылки [ править ]

  • crypt_blowfish, реализация, поддерживаемая Openwall