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 ENGINE structure.
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 callback;
The ENGINE_set_asyms(ENGINE *e, ENGINE_ASYMS_PTR f), which sets
a callback;
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.
Code changes
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()
function from the EVP_ASYMMETRIC structure.
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
DSA-like algorithms.
The patch provides the PKCS7_add_sign() function that allows adding the 2nd/3rd/etc signature to an existing signed PKCS#7 structure.
The generation of a detached signature for large files has been fixed
(OpenSSL
bug #1071).
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 structure.
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 commands);
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 structure has been freed
before the lh_delete function tries to delete the OBJ_NAME structure 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.
The EVP_ASYMMETRIC API 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).
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, BIO* bio_err);
-
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 algorithm.
int (*d2i_pub_key) (EVP_PKEY *key, const unsigned char **buf, long length);
-
Converts public keys from the X.509 certificate notation to the
internal notation.
The buf parameter should point to a copy of the pointer to the
buffer as described in FAQ.
The function is used to generate an X.509 certificate for this algorithm.
int (*a2i_algor_params) (EVP_PKEY *pkey, const ASN1_TYPE *param);
-
Parses algorithm parameters from a certificate or a private key.
Saves algorithm-specific parameters in the appropriate field of the
EVP_PKEY structure and sets true to
the save_parameters field.
If the algorithm doesn't save its parameters in a certificate, this
function may be undefined.
int (*d2i_priv_key) (EVP_PKEY *key,
const unsigned char **buf, long length);
-
Extracts a private key from the OCTET STRING of the key's DER
notation in PKCS#8.
The buf parameter should point to a copy of the pointer to the
buffer as described in FAQ.
This function is designed as a separate function, not a part of the
PKCS82pkey function, because this function is necessary for the
d2i_PrivateKey() API function.
int (*PKCS82pkey) (EVP_PKEY *key, PKCS8_PRIV_KEY_INFO *p8);
Extracts a private key with its parameters from PKCS#8
structure.
int (*i2d_pub_key) (EVP_PKEY *key, unsigned char **buf);
-
Packs a public key into an ASN1 structure.
The function is designed to create certificates and certificate
requests.
int (*i2a_algor_params) (EVP_PKEY *key, ASN1_TYPE *param);
-
Saves algorithm parameters in the given ASN1 structure. The structure
should be allocated but not filled.
int (*i2d_priv_key) (EVP_PKEY *key, unsigned char **buf);
-
Packs a private key into the DER notation for PKCS#8.
This function is designed as a separate function, not a part of
the pkey2PKCS8 function, because this function is necessary for the
i2d_PrivateKey() API function.
int (*pkey2PKCS8) (EVP_PKEY *pkey, PKCS8_PRIV_KEY_INFO *p8);
-
Packs a private key with its parameters into PKCS#8
structure.
int (*i2d_signature_algor) (EVP_PKEY *key, X509_ALGOR* param);
-
Fills the signatureAlgorithm field of the SignerInfo structure in
CMS/PKCS7.
int (*pkcs7_key_transport_encrypt)
(EVP_PKEY *pubk,
const unsigned char *key,
int key_len,
ASN1_OCTET_STRING *data);
-
Encrypts a symmetric encryption key of an S/MIME message by the
given public key and packs the result into the
KeyTransRecipientInfo.encryptedKey.
This function is designed for using 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.
Developers guide
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 the
ENGINE structure for enumeration of
implemented algorithms.
To set type and method fields of EVP_ASYMMETRIC please 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(),
ECDSA_size()).
In the method structure you should define at least the
sign() and verify() functions. If you use the
app_data field, you also need 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
following functions:
d2i_pub_key()
a2i_algor_params()
d2i_priv_key()
PKCS82pkey()
i2d_pub_key()
i2a_algor_params()
i2d_priv_key()
pkey2PKCS8()
To save signatures you should define i2d_signature_algor() function.
To encrypt CMS you should define the
pkcs7_key_transport_encrypt() and
pkcs7_key_transport_decrypt() functions.
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. 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
filename command.