[devel] I: Cryptocom openssl patch

Konstantin A. Lepikhov =?iso-8859-1?q?lakostis_=CE=C1_altlinux=2Eorg?=
Вт Июн 14 09:55:00 MSD 2005


Hi!

Данное письмо будет интересно тем, кто занимается реализацией поддержки
национальной криптографии на базе пакета openssl. Как правило, если туда
добавляется поддержка каких-то новых алгоритмов, то приходится
модифицировать довольно много кода самой библиотеки, в результате чего
конечная сертификация продукта на базе openssl сводится к сертификации
_всего_ кода openssl с национальной криптографией. Не так давно,
российская компания "Криптоком" (www.cryptocom.ru), опубликовала в списке
рассылки разработчиков openssl-dev довольно интересный патч, позволяющий
разнести реализацию криптоалгоритмов и код криптобиблиотеки. Сделано это
через расширенный интерфейс ENGINE, который уже присутствовал в openssl,
но был завязан только на аппаратные реализации симметричных шифров. Чем
это удобно - можно теперь сертифицировать только криптомодуль, а не весь
код openssl или приложения, что значительно сокращает сроки подготовки
ПО на базе openssl. Детали реализации изложены в приложенном драфте
описания патча. Пока у самой компании довольно много планов относительно
данного патча, но наиболее приоритетным считается один - чтобы этот патч
вошел в openssl upstream или, в крайнем случае, в contrib. Для этого
предлагается (с согласия участников ALTLinux Team) организовать
специальную ветку в Сизифе для тестирования и популяризации данного патча.
Если у кого-то из Team есть предложения по дальнейшей работе в данном
направлении (например, готовое решение на базе измененного openssl), прошу
делится этими мыслями, разработчики из Криптоком данный спискок тоже
читают (через архивы).

-- 
WBR, Konstantin	      chat with ==>ICQ: 109916175
     Lepikhov,	      speak  to ==>JID: lakostis на jabber.org
aka L.A. Kostis       write  to ==>mailto:lakostis на pisem.net.nospam

...The information is like the bank... 			  (c) EC8OR
----------- следующая часть -----------
Предыстория
~~~~~~~~~~~
В сообщении
http://marc.theaimsgroup.com/?l=openssl-dev&m=109947214028600&w=2 
мы описывали наши предложения по генерализации архитектуры работы с
асимметричными алгоритмами. В данном сообщении мы представляем очередную 
версию нашего патча, реализующую наши предложения. 

Обусловленность задачи
~~~~~~~~~~~~~~~~~~~~~~
Задача интересна прежде всего с точки зрения возможности легкого
добавления в openssl национальной криптографии.

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

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

Кроме того, наличие стандартизированного и документированного интерфейса
для добавления новых алгоритмов упростит разработку и тестирование
реализаций ранее неподдерживаемых алгоритмов и увеличит надежность этих
модификаций, так как для добавления нового алгоритма не будет
требоваться внесение многочисленных модификцаций в ядро OpenSSL.

Новые возможности, добавляемые патчем
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Цель данного патча - сделать возможным добавление в виде подгружаемых
модулей engine не только новых реализаций существующих ассиметричных
криптографических алгоритмов (DSA, RSA, ECDSA) но и добавление новых
асимметричных алгоритмов. Существующее API OpenSSL позволяет только
выбирать ту или иную реализацию стандартных алгоритмов, предоставляемую
разработчиками engine.

В частности, наша компания смогла, опираясь на данный патч, реализовать
для OpenSSL российские криптографические алгоритмы электронной подписи
GOST-1994, GOST-2001.
Мы протестировали для нашей реализации всю функциональность PKI и работу
с подписанными и зашифрованными сообщениями (SMIME). 

Описание патча
~~~~~~~~~~~~~~
Предлагаемый патч делает работу с асимметричными алгоритмами на уровне
ядра OpenSSL полностью аналогичной работе с симметричными алгоритмами и
алгоритмами дайджеста. 

Патч решает проблему использования одного
алгоритма дайджеста с несколькими алгоритмами подписи без клонирования
и заведения отдельных OID для, по сути своей, нескольких экземпляров
одного алгоритма. Идея аналогична присутствующей в закомментированном
виде в evp.h структуре EVP_PKEY_MD.

Патч вводит структуру EVP_ASYMMETRIC, аналогичную EVP_CIPHER и
EVP_MD и API управления данной структурой, аналогичное имеющемуся
для EVP_CIPHER/EVP_MD.

Эта структура содержит
- NID реализуемого алгоритма, 
- Тип реализуемого алгоритма (RSA/DSA/EC/DH),
- NID используемого с ним алгоритма дайджеста, если алгоритм, как DSA или
  российские алгоритмы, требует специфического дайджеста (DSA требует SHA1,
  российские алгоритмы подписи - российских алгоритмов дайджеста),
- NID используемой комбинации "алгоритм подписи - алгоритм дайджеста", в 
	случае, если это не удается определить из поля pkey_type
	соответствующего дайджеста.
- указатели на специфичные для алгоритмов функции, осуществляющие:
	- разбор параметров генерации ключа (используются в команде 
	  openssl req) (синтаксис опции -newkey генерализован и сделан
	  расширяемым),
	- конвертацию из представления в X509-сертификате во внутреннее
	  представление OpenSSL и обратно открытого ключа,
	- конвертацию из представления в X509-сертификате во внутреннее
	  представление OpenSSL и обратно параметров алгоритма,
	- конвертацию из представления в PKCS8-формате во внутреннее
	  представление OpenSSL и обратно секретного ключа,
	- сохранение ASN1-параметров алгоритма в SIGNER_INFO
	  PKCS7-структуры,
	- сохранение и разбор информации о получателе зашифрованного
	  сообщения.
	- выработку и проверку подписи.

Патч не реализует предложенную в нашем оригинальном пропозале
генерализацию работы с эллиптическими кривыми, так как нам оказалось
проще инкапсулировать преобразование параметров алгоритма ГОСТ 2001 в 
структуру EC_GROUP в вышеупомянутые функции соответствующего алгоритма.

Добавлена поддержка асимметричных алгоритмов на уровень ENGINE API, 
аналогичная существовавшим там функциям
ENGINE_get_ciphers/ENGINE_get_digests. Эти функции задействованы, в
частности, в команде openssl engine.

Все алгоритмы с открытым ключом, поддерживаемые OpenSSL (RSA, DSA, DH,
ECDSA, ECDH) переведены на работу через таблицу ассиметричных
алгоритмов. При этом комплект тестов, входящий в дистрибутив OpenSSL
полностью выполняется. 

API EVP_ASYMMETRIC и его использование
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	Все описанные в этом разделе функции, если не оговорено особо,
	возвращают положительное значение в случае успеха и неположительное -
	в случае ошибки.

	/* Recieves EVP_PKEY to store parameters, string from openssl req */
	/* options and BIO channel to report errors to */
	int (*parse_keygeneration_params)(EVP_PKEY *newkey,const char *params,
			BIO* bio_err);
	Принимает указатель на структуру EVP_PKEY, в которую при успехе
	сохраняются параметры, строковое представление параметров и BIO,
	используемое для выдачи сообщений об ошибках. 
	Функция предназначена для разбора параметра командной строки команды
	openssl req. Эта функция должна быть определена для того, чтобы была
	возможность создания X509-сертификатов с алгоритмом, соответствующим 
	данному EVP_ASYMMETRIC. Возвращает длину ключа в случае успеха.
	
	Эта фукнция используется для замены условной компиляции файла req.c.

	/* Parses ASN1_STRUCTRE of public key (typically from X509 cert) */
	int (*d2i_pub_key)(EVP_PKEY *key, const unsigned char *buf, long length);
	Эта функция используется для приведения прочитанного из сертификата
	открытого ключа ко внутреннему представлению. Должна быть определена
	для всех алгоритмов, для которых есть возможность создания
	сертификатов.

	/* Parses private key ASN1 STRUCTURE. Handles some cases of broken */
	/* PKCS8 returning appropriate constant in the int *broken field */
	int (*d2i_PKCS8_key)(EVP_PKEY *key, const unsigned char *buf, long length, int *broken);
	Эта функция используется для приведения прочитанного из сертификата
	секретного ключа ко внутреннему представлению. Должна быть определена
	для всех алгоритмов, для которых есть возможность создания
	сертификатов. Обрабатывает legacy-форматы секретных ключей.

	/* Parses ASN1 STRUCTURE of algorithm parameters  */
	/* If algorithm doesn't need parameters, this field could be NULL. */
	/* Otherwise it should allocate algorithm-type specific parameter */
	/* structure and assign it to pkey->pkey. It should also set  */
	/* save_parameters field of pkey to true. */
	int (*d2i_algor_params) (EVP_PKEY *pkey,const ASN1_TYPE *param);
	Функция предназначена для разбора хранимых в сертификате или секретном
	ключе параметров алгоритма. Если алгоритм не сохраняет свои параметры
	в сертификате, эту функцию можно не определять. В противном случае она
	должна сохранять алгоритм-специфичные параметры в соответствующем поле
	структуры EVP_PKEY и выставлять в true поле save_parameters.
	
	/* Packs public key into buffer according to algorithm specific */
	/* rules */
	int (*i2d_pub_key)(EVP_PKEY *key, unsigned char **buf);
	Функция выполняет упаковку открытого ключа в ASN1-структуру. Должна
	быть определена для возможности создания сертификатов.
	
	/* Packs private key into  buffer according to algorithm specific */
	/* rules */
	int (*i2d_priv_key)(EVP_PKEY *key, unsigned char **buf);
	Функция выполняет упаковку секретного ключа в PKCS8-структуру. Должна
	быть определена для возможности создания сертификатов.
	
	/* Fills given freshly allocated ASN1_TYPE structure with algorithm */
	/* parameters. Returns length of the structure. If param is NULL, */
	/* just returns length. Returns -1 on error. */
	int (*i2d_algor_params) (EVP_PKEY *key, ASN1_TYPE *param);
	Функция сохраняет параметры алгоритма в переданной ASN1-структуре. При
	передаче NULL возвращает необходимую длину.
	
	int (*i2d_signature_algor) (X509_ALGOR* param);
	Функция сохраняет NID алгоритма подписи и параметры подписи в переданную 
	структуру X509_ALGOR.

	/* S/MIME key encryption and pack */
	/* Returns non-positive value on error, positive on success */
	int (*pkcs7_key_transport_encrypt) (EVP_PKEY *pubk, const unsigned char *key,int key_len,  ASN1_OCTET_STRING *);
	Эта функция предназначена для упаковки ключа, с помощью которого
	выполняется симметричное шифрование сообщения, при отправке
	шифрованных S/MIME сообщений. Сейчас она рассчитана в соответствии с
	ограничениями текущей реализации OpenSSL на упаковку RSA-подобной
	структуры KeyTransRecipientInfo (RFC 2630, 6.2.1; драфты от
	"КриптоПро"). Должна быть определена в случае, когда алгоритм
	планируется использовать для создания зашифрованных S/MIME сообщений.

	Российские криптоалгоритмы используют KeyTransRecipientInfo для
	передачи эфемерных ключей из-за особенностей реализации Windows,
	рассматривающей любой алгоритм как RSA-подобный.
	
	/* S/MIME key unpack and decryption */
	/* Returns negative absolute value of required size on too small buffer 
	 * zero on error, decrypted key size on success*/
	int (*pkcs7_key_transport_decrypt) (EVP_PKEY* priv, unsigned char *key, int max_key_len, const ASN1_OCTET_STRING * data);
	Эта функция предназначена для распаковки симметричного ключа в
	зашифрованных S/MIME сообщениях. В случае недостаточного размера
	переданного буфера, если такая возможность есть, возвращает
	отрицательное значение, равное по абсолютной величине необходимой
	длине буфера. В случае успеха возвращает длину симметричного ключа.

	/* Signature generation. */
	/* Returns non-negative value on success. */
	int (*sign) (const EVP_PKEY *priv, int dgst_type, 
			const unsigned char *dgst_buf, unsigned int dgst_len,
			unsigned char *sigret, unsigned int *siglen);

	Вырабатывает подпись.

	/* Signature verification. */
	/* Returns non-negative value on success. */
	int (*verify) (const EVP_PKEY *pub, int dgst_type, 
			const unsigned char *dgst_buf, unsigned int dgst_len,
			const unsigned char *sigbuf, unsigned int siglen);

	Проверяет подпись.

	В случае, когда для EVP_ASYMMETRIC определяются функции sign и verify,
	рекомендуется определять их через соответствующие функции union'а
	method, чтобы обеспечить возможность легкой смены реализации через
	смену method (например, для аппаратных решений).

Добавлена функция  
EVP_PKEY* EVP_PKEY_by_asymmetric(const EVP_ASYMMETRIC *as); 
Она аналогична EVP_PKEY_new, за тем исключением, что выполняет более
полную инициализацию структуры EVP_PKEY, так как располагает большей
информацией.

В структуру ENGINE добавлен callback, отвечающий за работу с 
EVP_ASYMMETRIC.

В API ENGINE добавлены функции

ENGINE_ASYMS_PTR ENGINE_get_asyms(const ENGINE *e), аналогичная 
ENGINE_get_digests/ENGINE_get_ciphers, и возвращающая 
соответствующий callback, 
ENGINE_set_asyms(ENGINE *e, ENGINE_ASYMS_PTR f), устанавливающая
callback,

const EVP_ASYMMETRIC *ENGINE_get_asym(ENGINE *e, int nid), аналогичная 
ENGINE_get_digest/ENGINE_get_cipher, obtains an asymmetric implementation 
from an ENGINE functional reference,

ENGINE *ENGINE_get_asym_engine(int nid), аналогичная 
ENGINE_get_cipher_engine/ENGINE_get_digest_engine, возвращающая ENGINE,
в котором реализован алгоритм, относящийся к искомому nid.
	

Описание конкретных изменений
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
В коде libcrypto и утилиты openssl (команд, связанных с PKI и SMIME)
явные ветвления по типу алгоритма с открытым ключом
заменены на поиск алгоритма в таблице по его имени или OID-у и вызов
соответствующих функций по указателям, содержащимся в структуре
EVP_ASYMMETRIC. 
Благодаря этому удалось отказаться также от условной компиляции многих
файлов уровня EVP и выше в зависимости от макросов OPENSSL_NO_RSA,
OPENSSL_NO_DSA и OPENSSL_NO_EC.

Условная компиляция остается только при добавлении алгоритмов в таблицу
(файл crypto/evp/c_alla.c, добавляемый нашим патчем). Во всех остальных
ситуациях алгоритм, не поддерживаемый текущим вариантом компиляции
просто не находится в таблице, что приводит к соответствующему сообщению
об ошибке.

Явное прописывание дайджеста, жестко требуемого по спецификации того или
иного алгоритма, также заменено на поиск по значению из соответствующего 
поля структуры EVP_ASYMMETRIC.

Добавлена функция EVP_PKEY_by_asymmetric, создающая
новый экземпляр структуры EVP_PKEY и инициализирующая его в зависимости
от типа алгоритма.

В функциях EVP_SignFinal и EVP_VerifyFinal перед вызовом соответствующей
функции дайджеста проверяется, есть ли функция sign/verify у структуры
EVP_ASYMMETRIC, соответствующей переданному ключу.

В функциях ASN1_sign и ASN1_item_sign алгоритм связки "дайджест-подпись"
берется из дайджеста. Если результат - NID_undef, то он берется из
EVP_ASYMMETRIC, соответствующей переданному ключу.

Предлагаемый патч по умолчанию сохраняет секретные ключи
в формате PKCS8, так как этот формат универсален и позволяет работать с
любыми алгоритмами, в то время как непатченный OpenSSL по умолчанию
использует алгоритм-специфичные форматы секретных ключей. При экспорте
секретных ключей из PKCS12-структуры они также экспортируются в
PKCS8-формате.

Для преобразования в исторически сложившиеся форматы предполагается, как
и в исходной версии нашего предложения, использовать команду openssl pkcs8.

Кроме того, с целью упрощения логики создания PKCS8-файлов, нами
изменена логика записи DSA-ключей и RSA в формат PKCS8. В текущем снапшоте
создание PKCS8 структуры для DSA, удовлетворяющей стандарту, является
частным случаем создания broken структуры. Т.е. 
функция EVP_PKEY2PKCS8 вызывает функцию EVP_PKEY2PKCS8_broken.

В результате применения нашего патча функция EVP_PKEY2PKCS8 создает
корректную с точки зрения стандарта структуру, а функция
EVP_PKEY2PKCS8_broken сначала создает стандартную структуру вызовом
EVP_PKEY2PKCS8, а потом, если необходимо breaks it.

Команде openssl smime добавлена интерпретация ключей -algname, где имя
алгоритма в зависимости от осуществляемой операции может быть либо
алгоритмом дайджеста, либо алгоритмом шифрования.

При реализации работы с S/MIME мы опирались на специфицированные в
draft'ах "Соглашения о совместимости СКЗИ российских производителей".
Хотя используемая семантика соответствует KeyAgreement (RFC2630, 6.2.2),
эти документы специфицируют расширение формата KeyTransport (RFC2630,
6.2.1). Это связано, в частности, с необходимостью встраивания в
Microsoft Windows, где применяется RSA-подобная схема.
Эта же функциональность реализована в OpenSSL (до нашего патча
там поддерживались только RSA-ключи получателя).

К сожалению, в процессе разработки патча мы столкнулись с проблемами,
возникающими при создании объектов функцией OBJ_create изнутри
динамически подгружаемых библиотек.

Проблема заключается в том, что необходимо, чтобы динамически созданные
внутри engine объекты существовали
  а) в момент разбора командной строки openssl, так как они определяют
  допустимый набор параметров команд dgst, enc и req (после
  генерализации опции -newkey)
  б) в момент выполнения EVP_cleanup, так как при помещении алгоритмов в
  соответствующие таблицы функциями EVP_add_cipher, EVP_add_digest,
  EVP_add_asymmetric в поле name структуры OBJ_NAME прписывается ссылка
  на поле sn либо ln структуры ASN1_OBJECT. Соответственно, есил
  динамически размещенный объект, на который ссылается OBJ_NAME уже
  освобожден в тот момент, когда мы пытаемся удалить OBJ_NAME из таблицы
  с помощью функции lh_delete, происходит segmentation fault.

Для того, чтобы преодолеть эту проблему, мы перенесли очистку таблицы
объектов из отдельных процедур, реализующих команды openssl (req_main,
x509_main, ca_main etc) в код процедуры main файла openssl.c ПОСЛЕ вызова
apps_shutdown.

Reference implementation of engine
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Планы на будущее
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
В предлагаемом патче не реализована поддержка национальных криптографических
алгоритмов собственно в протоколе SSL. Обеспечение такой поддержки в текущей
архитектуре этой части OpenSSL является довольно сложной задачей, поэтому нам
показалось целесообразным выделить эти работы в отдельный этап.

Кроме того, предлагаемый патч обеспечивает возможность использования
национальных криптографических алгоритмов после подгрузки реализующего их
модуля engine.  В существующих программах, использующих OpenSSL, такая
подгрузка не предусмотрена. Эта проблема может быть решена, если OpenSSL будет
собрана с необходимым модулем engine. Однако данное решение неудобно для тех,
кто использует бинарные дистрибутивы программного обеспечения (например,
дистрибутивы ОС Linux) и не хочет терять совместимости с системой обновления
соответствующих дистрибутивов. 

Поэтому в будущем мы планируем усовершенствовать возможность управление 
подгрузкой модулей engine через файлы конфигурации.

Мы планируем реализовать поддержку российских алгоритмов в PKCS12 (в
настоящее время используются классические алгоритмы) и поддержку
экспорта зашифрованных секретных ключей с использованием предоставляемых
engine алгоритмов.

----------- следующая часть -----------
Было удалено вложение не в текстовом формате...
Имя     : =?iso-8859-1?q?=CF=D4=D3=D5=D4=D3=D4=D7=D5=C5=D4?=
Тип     : application/pgp-signature
Размер  : 189 байтов
Описание: =?iso-8859-1?q?=CF=D4=D3=D5=D4=D3=D4=D7=D5=C5=D4?=
Url     : <http://lists.altlinux.org/pipermail/devel/attachments/20050614/973310c8/attachment-0001.bin>


Подробная информация о списке рассылки Devel