We ask OpenSSL team to place this patch into the contrib section of
We claim that our contribution supplies the developers with a new
well-defined API, thus making the task of adding an arbitrary national
cryptographic algorithms to OpenSSL much more easier. Moreover, it
increases the reliability of such implementation because from now
there's no need to make a lot of modifications in OpenSSL code to
implement any exotic cryptographic standards.
New features added by the patch
1. The patch provides a possibility to add new asymmetric algorithms as
loading engines. Previous API allows to add a new implementation of
existing algorithms only (RSA, DSA, ECDSA). With our patch we have
managed to implement two Russian digital signature algorithms GOST-1994
2. The patch allows to use other algorithms in addition to RSA for key
transport in encrypted S/MIME messages.
3. The patch provides a possibility to create CMS messages with several
4. The patch solves the problem of using the same digest algorithm with
different signature algorithms without digest algorithm (EVP_MD
structure)cloning. The presentation of the idea can be found in the
EVP_PKEY_MD structure in the evp.h file.
5. The patch significantly reduces the memory demands for signing large
We have tested all PKI and S/MIME functionality for the patched
Description of the patch
The patch makes interface operations with asymmetric algorithms in
OpenSSL core similar to the interface operations with the symmetric and
The patch adds the EVP_ASYMMETRIC structure and the API designed to
control this structure. The structure and the API are similar to the
EVP_CIPHER and EVP_MD.
The EVP_ASYMMETRIC structure includes:
- the algorithm NID;
- the algorithm type (RSA-like, DSA-like, ECDSA-like or DH-like);
- pointers to the specific functions of the algorithm.
These functions are designed:
- to check compatibility of the asymmetric algorithm and the digest
algorithm (for example, DSA requires DSS1, Russian signature
algorithms require the Russian digest algorithm);
- to determine the NID of the 'asymmetric-digest' pair;
- to parse key generation parameters in the openssl req command
(syntax of the -newkey option has been generalized and made
- to convert public keys from the X.509 certificate notion to the
internal notation and vice versa;
- to convert the algorithm parameters from the X.509 certificate
notion to the internal notion and vice versa;
- to convert private keys from the PKCS#8 notation to the internal
notion and vice versa;
- to save ASN1 parameters of the algorithm in the SIGNER_INFO field
of in the PKCS#7 structure;
- to save and parse transport encoding of message encryption key;
- to sign and to verify signatures.
All the public key algorithms previously supported by OpenSSL (i.e. RSA,
DSA and ECDSA) have been translated to the asymmetric API. The native
tests have passed successfully.
The EVP_PKEY* EVP_PKEY_by_asymmetric (const EVP_ASYMMETRIC *as) function
has been added. This function is similar to the EVP_PKEY_new function
but contains more information and can initialize the EVP_PKEY structure
The EVP_PKEY structure has been changed. The type and save_type fields
have been removed and changed to the pointer to the appropriate
EVP_ASYMMETRIC. The EVP_PKEY_get_type and EVP_PKEY_get_save_type macros
have been added.
A callback to operate with the EVP_ASYMMETRIC has been added to the
The following functions have been added to the ENGINE API:
The ENGINE_ASYMS_PTR ENGINE_get_asyms(const ENGINE *e) is an analogue of
the ENGINE_get_digests/ENGINE_get_ciphers. It returns the corresponding
The ENGINE_set_asyms(ENGINE *e, ENGINE_ASYMS_PTR f), which sets
The const EVP_ASYMMETRIC *ENGINE_get_asym(ENGINE *e, int nid) is an
analogue of the ENGINE_get_digest/ENGINE_get_cipher. It returns the
implementation of the asymmetric algorithm from the engine.
These functions are used in particular in the 'openssl engine' command.
The ENGINE *ENGINE_get_asym_engine(int nid) is an analogue of the
ENGINE_get_cipher_engine/ENGINE_get_digest_engine. It returns ENGINE
providing the algorithm defined by nid.
In the code of the libcrypto library and of the openssl utility obvious
branchings depending on the algorithm type have been replaced by
searching the algorithm in the table by its name or by its OID and
calling the corresponding functions from the EVP_ASYMMETRIC structure.
As a result it's now possible to remove conditional compilation of many
files on the EVP and higher levels depending on the OPENSSL_NO_RSA,
OPENSSL_NO_DSA and OPENSSL_NO_EC preprocessor definitons. Conditional
compilation is necessary only when adding algorithms to the table (the
crypto/evp/c_alla.c file added by our patch). In any other case an
algorithm that is not supported by the current build simply can't be found in the table and the corresponding error message appears.
Instead of hardcoding of digest algorithm for particular signature
algorithm the patch allows to call the compatible_digest() from the
The EVP_SignFinal() and EVP_VerifyFinal() functions check if there is a
sign()/verify() function in the EVP_ASYMMETRIC structure associated with
the passed key. If such a function exists, this function will be used
instead of the function from the EVP_MD structure. It is very useful if
several signature algorithms require the same digest algorithm. For
example the DSA and ECDSA algorythms require the SHA-1 digest
algorithm. Such architecture allows to evade 'cloning'
the digest algorithm structure and hardcode substitutions of EVP_MD
structures or OIDs, as it is in the current SHA-1 implementation. The
implementation of SHA-1 hasn't been fixed yet.
The ASN1_sign() and ASN1_item_sign() functions get the NID of the
"digest-signature" pair by calling the pkey_type_f() function of the
EVP_ASYMMETRIC structure associated with the given key. It's also useful
when using the same digest algorithm with different signature algorithms.
The private keys for new algorithms are saved in the PKCS#8 format, as
this format is suitable for any algorithm. For the previously supported
algorithms the old behavior has been kept, i.e. algorithm-specific
formats of private keys are used. The behavior of exporting private keys
from PKCS#12 is similar.
We've found it useful to add the app_data field to the DSA structure.
This field can be used for saving implementation-specific data of
The patch provides the PKCS7_add_sign() function that allows adding the
2nd/3rd/etc signature to an existing signed PKCS#7 structure.
The patch adds the -cipheralg key for the 'openssl smime -encrypt'
command. This key allows specifying the name of the cipher algorithm
used for an operation. Moreover, any unknown parameter looking like
-<word> is interpreted as '-cipheralg <word>'.
The patch adds the -add parameter for the 'openssl smime -sign' command.
This parameter is used to add a signature to an existing signed PKCS#7
Unfortunately we faced a problem of object creation by the OBJ_create
function in dynamically loaded libraries. The objects dynamically
created in an engine should exist
a) at the moment of parsing the openssl command line (since they
define the set of possible parameters of the dgst, enc and req
b) at the moment of running the EVP_cleanup (when the EVP_add_cipher,
EVP_add_digest and EVP_add_asymmetric functions add an algorithm to
the tables, they write a reference to the sn or ln field of the
ASN1_OBJECT structure to name the field of the OBJ_NAME structure). If a dynamically allocated object referred by the OBJ_NAME has been freed
before the lh_delete function tries to delete the OBJ_NAME from the
table, a segmentation fault occurs.
To solve this problem, we've moved the table cleaning code from the
procedures implementing the particular openssl commands (req_main(),
x509_main(), ca_main() etc) to the main() procedure of the openssl.c
file AFTER the call of the apps_shutdown() function.
API EVP_ASYMMETRIC and using it
EVP_ASYMMETRIC is a basic structure designed for adding new asymmetric
algorithms to the OpenSSL. In this section we describe the structure
callback functions in detail. We also indicate functions that should be
defined for some specific cryptographic operations.
All functions return a positive value in case of success, 0 or negative
otherwise (if something else hasn't been mentioned).
int (*compatible_digest) (int candidate);
The function returns the NID of the digest algorithm compatible with
the asymmetric algorithm (as signature algorithm), preferring the
candidate parameter (that is, if the returned value is not equal to
the candidate, the candidate is incompatible).
Designed to choose a compatible digest when signing and to check
compatibility when verifying signature.
int (*pkey_type_f) (int dgst_nid);
Returns the NID of the pair 'this signature algorithm - a given
digest algorithm'. This NID will be mapped to the
signatureAlgorithm.algorithm field of the X.509 certificate.
int (*parse_keygeneration_params)(EVP_PKEY *newkey, const char *params,
Parses the -newkey parameter of the 'openssl req' command. Returns
the key length in bits in case of success, 0 otherwise. Saves
parameters in the EVP_PKEY structure pointed by the newkey. Outputs
error messages to the bio_err channel.
The function is designed to generate an X.509 certificate for this
int (*d2i_pub_key)(EVP_PKEY *key, const unsigned char **buf, long length);
Converts public keys from the X.509 certificate notion to the
Encrypts a symmetric encryption key of an S/MIME message by the
given public key and packs the result into the
This function is used in message-encrypting operations.
int (*pkcs7_key_transport_decrypt) (EVP_PKEY* priv,
unsigned char *key, int max_key_len, const ASN1_OCTET_STRING * data);
Unpacks and decrypts the symmetric key from an encrypted S/MIME message.
Returns the length of the symmetric key in case of 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);
Signs the given digest.
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);
Verifies a signature on the given digest.
We recommend to implement the sign() and verify() functions of the
EVP_ASYMMETRIC structure through corresponding functions of the
method union. It is necessary for an easy change of implementation
(e.g. to the hardware implementation) by changing of method.
To develop an engine providing new asymmetric algorithms, you should:
- register all necessary OIDs by calling the OBJ_create() function or
declaring them in the OpenSSL configuration file;
- declare necessary structures for implementing algorithms: EVP_MD for
the digest algorithms, EVP_CIPHER for the symmetric algorithms,
EVP_ASYMMETRIC for the asymmetric algorithms, and set appropriate NIDs
in this structures;
- implement callback functions of ENGINE structure for enumeration of
To set type and method fields of EVP_ASYMMETRIC please have in mind that
- a key pair and algorithm parameters should be easily represented by
the chosen structure (RSA, DSA or ECDSA);
- a signature size is calculated differently for the RSA algorithm and
for the El-Gamal algorithm (functions RSA_size(), DSA_size(),
In the method structure you should define at least the sign() and
verify() functions. If you use the app_data field, you need also the
init() and finish() functions.
You should define the compatible_digest() and pkey_type_f() functions
according to the specification.
To make certificates by the openssl command-line utility you should define
the parse_keygeneration_params() function.
To operate with certificates and private keys you should define the
To save signatures you should define i2d_signature_algor() function.
To encrypt CMS you should define the pkcs7_key_transport_encrypt() and
In the EVP_ASYMMETRIC structure the sign() and verify() functions should
be defined if different signature algorithms should be used with the
same digest algorithm. We recommend to implement these functions
through the corresponding functions of the method structure.
We provide the engine test suite that can be used to check your engine
(see http://www.cryptocom.ru/OpenSource/test-suite.tar.gz). To use this
test suite you should set correct algorithm names and a correct hash
value for the testing data. The test suite uses etalon ASN1-files, these
files should be prepared in advance using the 'openssl asn1parse -i -in
The patch doesn't provide a possibility to use national cryptographic
algorithms in TLS. It seems to us rather difficult to provide such a
possibility in the existing architecture of OpenSSL at once, that's why
we decided to solve the problem step by step.
The patch doesn't allow to use new cipher algorithms to protect data in
the PKCS#12 structure.
It is reasonable to add a general algorithm type
EVP_PKEY_ENGINE_DEFINED. It will permit to support any asymmetric
algorithm, not only RSA-like and El-Gamal-like.