Scroll to navigation

fr::crypto::engine(3SSL) OpenSSL fr::crypto::engine(3SSL)

NOM

moteur - ENGINE module de support cryptographique

SYNOPSIS

 #include <openssl/engine.h>
 ENGINE *ENGINE_get_first(void);
 ENGINE *ENGINE_get_last(void);
 ENGINE *ENGINE_get_next(ENGINE *e);
 ENGINE *ENGINE_get_prev(ENGINE *e);
 int ENGINE_add(ENGINE *e);
 int ENGINE_remove(ENGINE *e);
 ENGINE *ENGINE_by_id(const char *id);
 int ENGINE_init(ENGINE *e);
 int ENGINE_finish(ENGINE *e);
 void ENGINE_load_openssl(void);
 void ENGINE_load_dynamic(void);
 #ifndef OPENSSL_NO_STATIC_ENGINE
 void ENGINE_load_4758cca(void);
 void ENGINE_load_aep(void);
 void ENGINE_load_atalla(void);
 void ENGINE_load_chil(void);
 void ENGINE_load_cswift(void);
 void ENGINE_load_gmp(void);
 void ENGINE_load_nuron(void);
 void ENGINE_load_sureware(void);
 void ENGINE_load_ubsec(void);
 #endif
 void ENGINE_load_cryptodev(void);
 void ENGINE_load_builtin_engines(void);
 void ENGINE_cleanup(void);
 ENGINE *ENGINE_get_default_RSA(void);
 ENGINE *ENGINE_get_default_DSA(void);
 ENGINE *ENGINE_get_default_ECDH(void);
 ENGINE *ENGINE_get_default_ECDSA(void);
 ENGINE *ENGINE_get_default_DH(void);
 ENGINE *ENGINE_get_default_RAND(void);
 ENGINE *ENGINE_get_cipher_engine(int nid);
 ENGINE *ENGINE_get_digest_engine(int nid);
 int ENGINE_set_default_RSA(ENGINE *e);
 int ENGINE_set_default_DSA(ENGINE *e);
 int ENGINE_set_default_ECDH(ENGINE *e);
 int ENGINE_set_default_ECDSA(ENGINE *e);
 int ENGINE_set_default_DH(ENGINE *e);
 int ENGINE_set_default_RAND(ENGINE *e);
 int ENGINE_set_default_ciphers(ENGINE *e);
 int ENGINE_set_default_digests(ENGINE *e);
 int ENGINE_set_default_string(ENGINE *e, const char *list);
 int ENGINE_set_default(ENGINE *e, unsigned int flags);
 unsigned int ENGINE_get_table_flags(void);
 void ENGINE_set_table_flags(unsigned int flags);
 int ENGINE_register_RSA(ENGINE *e);
 void ENGINE_unregister_RSA(ENGINE *e);
 void ENGINE_register_all_RSA(void);
 int ENGINE_register_DSA(ENGINE *e);
 void ENGINE_unregister_DSA(ENGINE *e);
 void ENGINE_register_all_DSA(void);
 int ENGINE_register_ECDH(ENGINE *e);
 void ENGINE_unregister_ECDH(ENGINE *e);
 void ENGINE_register_all_ECDH(void);
 int ENGINE_register_ECDSA(ENGINE *e);
 void ENGINE_unregister_ECDSA(ENGINE *e);
 void ENGINE_register_all_ECDSA(void);
 int ENGINE_register_DH(ENGINE *e);
 void ENGINE_unregister_DH(ENGINE *e);
 void ENGINE_register_all_DH(void);
 int ENGINE_register_RAND(ENGINE *e);
 void ENGINE_unregister_RAND(ENGINE *e);
 void ENGINE_register_all_RAND(void);
 int ENGINE_register_STORE(ENGINE *e);
 void ENGINE_unregister_STORE(ENGINE *e);
 void ENGINE_register_all_STORE(void);
 int ENGINE_register_ciphers(ENGINE *e);
 void ENGINE_unregister_ciphers(ENGINE *e);
 void ENGINE_register_all_ciphers(void);
 int ENGINE_register_digests(ENGINE *e);
 void ENGINE_unregister_digests(ENGINE *e);
 void ENGINE_register_all_digests(void);
 int ENGINE_register_complete(ENGINE *e);
 int ENGINE_register_all_complete(void);
 int ENGINE_ctrl(ENGINE *e, int cmd, long i, void *p, void (*f)(void));
 int ENGINE_cmd_is_executable(ENGINE *e, int cmd);
 int ENGINE_ctrl_cmd(ENGINE *e, const char *cmd_name,
         long i, void *p, void (*f)(void), int cmd_optional);
 int ENGINE_ctrl_cmd_string(ENGINE *e, const char *cmd_name, const char *arg,
         int cmd_optional);
 int ENGINE_set_ex_data(ENGINE *e, int idx, void *arg);
 void *ENGINE_get_ex_data(const ENGINE *e, int idx);
 int ENGINE_get_ex_new_index(long argl, void *argp, CRYPTO_EX_new *new_func,
         CRYPTO_EX_dup *dup_func, CRYPTO_EX_free *free_func);
 ENGINE *ENGINE_new(void);
 int ENGINE_free(ENGINE *e);
 int ENGINE_up_ref(ENGINE *e);
 int ENGINE_set_id(ENGINE *e, const char *id);
 int ENGINE_set_name(ENGINE *e, const char *name);
 int ENGINE_set_RSA(ENGINE *e, const RSA_METHOD *rsa_meth);
 int ENGINE_set_DSA(ENGINE *e, const DSA_METHOD *dsa_meth);
 int ENGINE_set_ECDH(ENGINE *e, const ECDH_METHOD *dh_meth);
 int ENGINE_set_ECDSA(ENGINE *e, const ECDSA_METHOD *dh_meth);
 int ENGINE_set_DH(ENGINE *e, const DH_METHOD *dh_meth);
 int ENGINE_set_RAND(ENGINE *e, const RAND_METHOD *rand_meth);
 int ENGINE_set_STORE(ENGINE *e, const STORE_METHOD *rand_meth);
 int ENGINE_set_destroy_function(ENGINE *e, ENGINE_GEN_INT_FUNC_PTR destroy_f);
 int ENGINE_set_init_function(ENGINE *e, ENGINE_GEN_INT_FUNC_PTR init_f);
 int ENGINE_set_finish_function(ENGINE *e, ENGINE_GEN_INT_FUNC_PTR finish_f);
 int ENGINE_set_ctrl_function(ENGINE *e, ENGINE_CTRL_FUNC_PTR ctrl_f);
 int ENGINE_set_load_privkey_function(ENGINE *e, ENGINE_LOAD_KEY_PTR loadpriv_f);
 int ENGINE_set_load_pubkey_function(ENGINE *e, ENGINE_LOAD_KEY_PTR loadpub_f);
 int ENGINE_set_ciphers(ENGINE *e, ENGINE_CIPHERS_PTR f);
 int ENGINE_set_digests(ENGINE *e, ENGINE_DIGESTS_PTR f);
 int ENGINE_set_flags(ENGINE *e, int flags);
 int ENGINE_set_cmd_defns(ENGINE *e, const ENGINE_CMD_DEFN *defns);
 const char *ENGINE_get_id(const ENGINE *e);
 const char *ENGINE_get_name(const ENGINE *e);
 const RSA_METHOD *ENGINE_get_RSA(const ENGINE *e);
 const DSA_METHOD *ENGINE_get_DSA(const ENGINE *e);
 const ECDH_METHOD *ENGINE_get_ECDH(const ENGINE *e);
 const ECDSA_METHOD *ENGINE_get_ECDSA(const ENGINE *e);
 const DH_METHOD *ENGINE_get_DH(const ENGINE *e);
 const RAND_METHOD *ENGINE_get_RAND(const ENGINE *e);
 const STORE_METHOD *ENGINE_get_STORE(const ENGINE *e);
 ENGINE_GEN_INT_FUNC_PTR ENGINE_get_destroy_function(const ENGINE *e);
 ENGINE_GEN_INT_FUNC_PTR ENGINE_get_init_function(const ENGINE *e);
 ENGINE_GEN_INT_FUNC_PTR ENGINE_get_finish_function(const ENGINE *e);
 ENGINE_CTRL_FUNC_PTR ENGINE_get_ctrl_function(const ENGINE *e);
 ENGINE_LOAD_KEY_PTR ENGINE_get_load_privkey_function(const ENGINE *e);
 ENGINE_LOAD_KEY_PTR ENGINE_get_load_pubkey_function(const ENGINE *e);
 ENGINE_CIPHERS_PTR ENGINE_get_ciphers(const ENGINE *e);
 ENGINE_DIGESTS_PTR ENGINE_get_digests(const ENGINE *e);
 const EVP_CIPHER *ENGINE_get_cipher(ENGINE *e, int nid);
 const EVP_MD *ENGINE_get_digest(ENGINE *e, int nid);
 int ENGINE_get_flags(const ENGINE *e);
 const ENGINE_CMD_DEFN *ENGINE_get_cmd_defns(const ENGINE *e);
 EVP_PKEY *ENGINE_load_private_key(ENGINE *e, const char *key_id,
     UI_METHOD *ui_method, void *callback_data);
 EVP_PKEY *ENGINE_load_public_key(ENGINE *e, const char *key_id,
     UI_METHOD *ui_method, void *callback_data);
 void ENGINE_add_conf_module(void);

DESCRIPTION

Ces fonctions créent, manipulent et utilisent des modules cryptographiques sous la forme d'objets ENGINE. Ces objets servent de conteneurs pour les implémentations d'algorithmes cryptographiques, et ils gèrent un mécanisme de comptage de référence qui les autorisent à être chargés de façon dynamique dans l'application exécutée et en dehors.

La fonctionnalité cryptographique qui peut être fournie par l'implémentation d'un ENGINE inclut les abstractions suivantes :

 RSA_METHOD - pour fournir une implémentation RSA alternative
 DSA_METHOD, DH_METHOD, RAND_METHOD, ECDH_METHOD, ECDSA_METHOD,
       STORE_METHOD - identique pour d'autres API d'OpenSSL
 EVP_CIPHER - potentiellement plusieurs algorithmes de chiffrement (indexé par 'nid')
 EVP_DIGEST - potentiellement plusieurs algorithmes de hachage (indexé par 'nid')
 key-loading - chargement des clés EVP_PKEY publiques/privées

Référence de calculs et traitements

À cause de la nature modulaire de l'API ENGINE, les pointeurs vers les ENGINE doivent être traités comme des références abstraites vers des ressources — c'est-à-dire pas seulement comme des pointeurs, mais aussi comme des références vers l'objet ENGINE sous-jacent. C'est-à-dire que l'on doit obtenir une nouvelle référence lors de la copie d'un pointeur vers un ENGINE si la copie doit être utilisée (et libérée) de façon indépendante.

Les objets ENGINE ont deux niveaux de comptage de références pour correspondre à la façon dont les objets sont utilisés. À plus bas niveau, chaque pointeur ENGINE est intrinsèquement une référence structurelle - une référence structurelle est nécessaire pour utiliser toute valeur de pointeur, car ce type de référence garantit que la structure ne peut pas être désallouée avant que sa référence ne soit libérée.

Mais, une référence structurelle n'offre aucune garantie que l'ENGINE soit initialisé et capable d'utiliser n'importe laquelle de ses implémentations cryptographiques. En effet, il est fort probable que la plupart des ENGINE ne puissent pas s'initialiser dans des environnements typiques, car les ENGINES sont généralement utilisés pour gérer du matériel spécialisé. Pour utiliser une fonctionnalité d'ENGINE, il est nécessaire d'avoir une référence fonctionnelle. Cette référence peut être considérée comme une forme spécialisée de référence structurelle, car chaque référence fonctionnelle contient de façon implicite une référence structurelle. Par contre, pour éviter d'avoir des programmes durs à déboguer, il est recommandé de traiter ces deux types de référence indépendamment l'une de l'autre. Si une référence fonctionnelle est disponible pour un ENGINE, il est garanti que cet ENGINE a été initialisé et est prêt à faire des opérations cryptographiques et restera non-initialisable jusqu’à la libération de la référence.

Structural references

Ce type de référence de base est utilisé pour instancier de nouveaux ENGINE, itérer sur les listes liées d'ENGINE chargés d'OpenSSL, lire les informations d'un ENGINE, etc. Une référence structurelle est essentiellement suffisante pour des requêtes ou pour manipuler les données d'implémentation d'un ENGINE au lieu d'utiliser ses fonctionnalités.

La fonction ENGINE_new() renvoie une référence structurelle vers un objet ENGINE vide. Il y a d'autres fonctions d'API dans ENGINE qui renvoient une référence structurelle comme : ENGINE_by_id(), ENGINE_get_first(), ENGINE_get_last(), ENGINE_get_next(), ENGINE_get_prev(). Toute référence structurelle devrait être libérée par un appel correspondant à la fonction ENGINE_free() — l'objet ENGINE lui-même ne sera nettoyé et désalloué que lorsque la dernière référence structurelle sera libérée.

On doit aussi remarquer que beaucoup d'appels de fonctions d'API d'ENGINE qui acceptent une référence structurelle obtiendront de façon interne une autre référence — en général cela ce produira lorsque l'ENGINE sera nécessaire à OpenSSL après le retour de la fonction. Exemple : la fonction d'ajout d'un nouvel ENGINE à la liste interne d'OpenSSL est ENGINE_add() — si cette actionse termine avec succès, alors OpenSSL aura stocké une nouvelle référence structurelle en interne, aussi l'appelant est toujours responsable de la libération de ses propres références avec ENGINE_free() quand les fonctions n'en ont plus besoin. D'une façon similaire, certaines fonctions libéreront automatiquement la référence structurelle qui leur est donnée si une partie du travail de la fonction est de faire cela. Exemple : les fonctions ENGINE_get_next() et ENGINE_get_prev() sont utilisées pour itérer à travers la liste interne d'ENGINE — elles renverront une nouvelle référence structurelle au prochain (ou précédent) ENGINE dans la liste ou NULL si elles sont à la fin (ou au début) de la liste, mais, dans tous les cas, la référence structurelle passée à la fonction est libérée au nom de l'appelant.

Pour éclaircir la façon dont une fonction traite les références, on doit toujours consulter la documentation en utilisant la page man de cette fonction, ou à défaut l’en-tête openssl/engine.h qui contient des indices.

Références fonctionnelles

Comme mentionné, les références fonctionnelles existent lorsque la fonctionnalité cryptographique d'un ENGINE doit être disponible. Une référence fonctionnelle peut être obtenue de plusieurs façons : d'une référence structurelle pré-existante demandée par l'ENGINE, ou en demandant à l'ENGINE par défaut d'OpenSSL pour une tâche cryptographique donnée.

Pour obtenir une référence fonctionnelle à partir d'une référence structurelle, appelez la fonction ENGINE_init(). Cela renvoie 0 si l'ENGINE n'était pas déjà opérationnel et ne pouvait donc pas être initialisé (ex : le manque de pilotes dans le système, pas de matériel spécifique attaché, etc), sinon elle renverra autre chose que 0 pour indiquer que l'ENGINE est maintenant opérationnel et a alloué une référence fonctionnelle à l'ENGINE. Toutes les références fonctionnelles sont libérées en appelant ENGINE_finish() (ce qui supprime les références structurelles implicites aussi).

La deuxième façon de récupérer des références fonctionnelles est de demander à OpenSSL une implémentation par défaut pour la tâche voulue, ex : avec ENGINE_get_default_RSA(), ENGINE_get_default_cipher_engine(), etc. Celles-ci sont expliquées dans la prochaine partie, mais elles ne sont d'habitude pas requises par les programmeurs d'application car elles sont utilisées automatiquement quand les spécificités de types de l'algorithme dans OpenSSL sont créées et utilisées, comme RSA, DSA, EVP_CIPHER_CTX, etc.

Implémentations par défaut

Pour chaque abstraction gérée, le code d'ENGINE maintient une table d'état de contrôle interne dans laquelle les implémentations sont disponibles pour une abstraction donnée et qui devrait être utilisée par défaut. Ces implémentations sont enregistrées dans les tables et indexées par une valeur « nid », parce que les abstractions comme EVP_CIPHER et EVP_DIGEST gèrent énormément d'algorithmes et de modes distincts, et les ENGINE peuvent arbitrairement tous les prendre en charge. Dans le cas d'autres abstractions comme RSA, DSA, etc, il n'y a qu'un seul « algorithme » donc toutes les implémentations sont enregistrées de façon implicite en utilisant le même index « nid ».

Quand une requête d'un ENGINE par défaut pour une abstraction/algorithme/mode est faite (par ex. en appelant RSA_new_method(NULL)), un appel « get_default » peut être fait au sous-système de l'ENGINE pour traiter l'état de la table correspondante et renvoyer une référence fonctionnelle vers un ENGINE initialisé dont l'implémentation devrait être utilisée. Si aucun ENGINE ne doit (ou ne peut) être utilisé, elle renverra NULL et l'appelant opérera avec un identificateur d'ENGINE NULL — cela correspond à utiliser l'implémentation conventionnelle d'un programme. Dans ce cas, OpenSSL fonctionnera de la même façon qu'avant que l'API ENGINE n'existe.

Chaque table d'état a un drapeau pour indiquer si elle a traité cette requête « get_default » depuis que la table a été modifiée, car pour traiter cette question elle doit itérer à travers tous les ENGINE enregistrés dans la table en essayant de les initialiser les uns après les autres, au cas où l'un d'entre eux soit opérationnel. Si elle renvoie une référence fonctionnelle vers un ENGINE, elle mettra en cache une autre référence pour accélérer le processus en cas de futures requêtes (plus besoin d'avoir à itérer sur toute la table). De même, elle mettra en cache une réponse NULL si aucun ENGINE n'est disponible pour que les futures requêtes ne fassent pas la même itération, sauf si l'état de la table change. Ce comportement peut aussi être changé ; si le drapeau ENGINE_TABLE_FLAG_NOINIT est défini (en utilisant ENGINE_set_table_flags()), il n'y aura pas de tentatives d'initialisation, et la seule façon pour une table d'état de renvoyer à un ENGINE non NULL pour la requête « get_default » sera celle qui sera indiquée de façon explicite dans la table. Exemple : ENGINE_set_default_RSA() fait la même chose qu'ENGINE_register_RSA() sauf qu'elle définit aussi le cache de réponses de la table pour la requête « get_default ». Dans ce cas, les abstractions comme EVP_CIPHER, où les implémentations sont indexées par un « nid », ces drapeaux et réponses cachés sont différents des valeurs « nid ».

Exigences de l'application

Cette partie expliquera les choses de base qu'un programmeur d'applications doit connaître pour rendre disponibles les éléments les plus utiles de la fonctionnalité d'ENGINE à l'utilisateur. La première chose à considérer est si le programmeur veut fournir un module alternatif d'ENGINE disponible à l'application et à l'utilisateur. OpenSSL maintient une liste liée interne d'ENGINE « visibles » qu'il a besoin d'opérer — au démarrage, cette liste est vide et d'ailleurs si une application n'utilise pas d'appel de l'API d'ENGINE et qu'elle utilise un lien statique envers openssl, alors le binaire de l'application résultante ne contiendra aucun code d'ENGINE alternatif. La première considération est donc de savoir si toute implémentation d'ENGINE doit être rendue visible à OpenSSL — cela est contrôlé en appelant les différentes fonctions de « chargement », exemple :

 /* Rendre l'ENGINE S<« dynamique »> disponible */
 void ENGINE_load_dynamic(void);
 /* Rendre le support d'accélération matériel CryptoSwift disponible */
 void ENGINE_load_cswift(void);
 /* Rendre le support de matériel S<« CHIL »> de nCipher disponible */
 void ENGINE_load_chil(void);
 ...
 /* Rendre toutes les implémentations du paquet OpenSSL disponibles. */
 void ENGINE_load_builtin_engines(void);

Après avoir appelé n'importe laquelle de ces fonctions, les objets ENGINE ont été alloués de façon dynamique, peuplés avec ces implémentations et liés à la liste chaînée interne d'OpenSSL. À partir de ce moment il est nécessaire de mentionner une fonction de l'API importante ;

 void ENGINE_cleanup(void);

Si aucune fonction d'API ENGINE n'est appelée dans une application, alors il n'y a pas de fuite mémoire dont il faut s'inquiéter dans les fonctionnalités d'ENGINE ; néanmoins, si n'importe quel ENGINE est chargé, même s'il n'est jamais enregistré ou utilisé, il est nécessaire d'utiliser la fonction ENGINE_cleanup() pour le nettoyer de façon correcte avant la fermeture du programme, si l'utilisateur veut éviter les fuites mémoires. Ce mécanisme utilise une table enregistrant les rétroactions internes pour qu'une fonctionnalité d'API ENGINE, qui sait qu'elle a besoin d'être nettoyée, puisse enregistrer les détails du nettoyage à appeler pendant l'appel deENGINE_cleanup(). Cette approche permet à ENGINE_cleanup() de nettoyer après n'importe quelle utilisation par un programme d'une fonctionnalité d'ENGINE, mais cela ne crée pas de lien de dépendance à toutes les fonctionnalités possibles d'ENGINE — seules les rétroactions de nettoyage nécessaires aux fonctionnalités utilisées, seront requises par le lieur.

Le fait que les ENGINES sont rendus visibles à OpenSSL (et de ce fait sont liés au programme et chargés en mémoire lors de l'exécution) ne veut pas dire qu'ils sont « enregistrés » ou appelés et utilisés par OpenSSL automatiquement — ce comportement est quelque chose qui doit être contrôlé par l'application. Certaines applications auront besoin d'autoriser l'utilisateur à spécifier exactement quel ENGINE il voudra utiliser si il y en a un qui doit être utilisé. D'autres préféreront tout charger pour que OpenSSL utilise automatiquement le premier ENGINE qui peut s'initialiser correctement — c'est-à-dire, qui puisse correspondre à une accélération matérielle attachée à la machine, ou quelque chose comme cela. Il y a probablement énormément d'autres façons par lesquelles l'application peut préférer gérer les choses, nous illustrerons donc simplement les conséquences telles qu'elles s'appliquent à des cas simples et laisserons les développeurs considérer ces cas et le code source de fonctions utilitaires d'OpenSSL comme guide.

Utiliser une implémentation spécifique d'ENGINE

Ici, nous allons supposer qu'une application a été configurée par son utilisateur ou administrateur pour utiliser l'ENGINE « ACME » s'il est disponible dans la version d'OpenSSL avec laquelle l'application a été compilée. S'il est disponible, il devra être utilisé par défaut pour tous les chiffrements RSA, DSA et les opérations par chiffrement symétrique, sinon OpenSSL devrait utiliser ses fonctionnalités internes comme d'habitude. Le code suivant illustre comment faire cela :

 ENGINE *e;
 const char *engine_id = "ACME";
 ENGINE_load_builtin_engines();
 e = ENGINE_by_id(engine_id);
 if(!e)
     /* le moteur n'est pas disponible */
     return;
 if(!ENGINE_init(e)) {
     /* le moteur n'a pas pu être initialisé, libérer « e » */
     ENGINE_free(e);
     return;
 }
 if(!ENGINE_set_default_RSA(e))
     /* Ceci devrait uniquement se produire quand S<« e »> ne peut être
      * initialisé, mais la déclaration précédente suggère qu'il l'a été. */
     abort();
 ENGINE_set_default_DSA(e);
 ENGINE_set_default_ciphers(e);
 /* Libère la référence fonctionnelle d'ENGINE_init() */
 ENGINE_finish(e);
 /* Libère la référence structurelle d'ENGINE_by_id() */
 ENGINE_free(e);

Utiliser automatiquement les implémentations intégrées d'ENGINE

Ici, nous allons supposer que l'on veut charger et enregistrer toutes les implémentations d'ENGINE fournies avec OpenSSL, de façon à ce que n'importe quel algorithme de cryptographie qui est utilisé par OpenSSL — s'il y a un ENGINE qui l'implémente et peut être initialisé — alors il sera utilisé. Le code suivant montre un exemple d'utilisation :

 /* Charger et rendre visible tous les paquets ENGINE dans la mémoire. */
 ENGINE_load_builtin_engines();
 /* Enregistrer tous les paquets pour tous les algorithmes qu'ils implémentent collectivement */
 ENGINE_register_all_complete();

Cela est tout ce qui est requis. Par exemple, la prochaine fois qu'OpenSSL essaye de mettre en place une clé RSA, n'importe quel ENGINE fourni qui implémente RSA_METHOD sera passé à ENGINE_init() et si l'un d'entre eux réussit, cet ENGINE sera choisi par défaut pour l'utilisation de RSA.

Support de configuration avancé

Il y a un mécanisme géré par le framework de l'ENGINE qui autorise chaque implémentation d'ENGINE de définir un nombre arbitraire d'ensembles de « commandes » de configuration, de les révéler à OpenSSL et toute application basée sur OpenSSL. Ce mécanisme est entièrement basé sur l'utilisation de couple noms-valeurs et suppose que l'entrée est au format ASCII (pas d'unicode ou d'UTF pour l'instant !), c'est donc idéal si l'application veut fournir une possibilité de fournir, d'une façon transparente aux utilisateurs, des « directives » de configuration arbitraires directement à ces ENGINE. Il permet aussi à l'application de demander de façon dynamique à l'implémentation de l'ENGINE des noms, descriptions et drapeaux d'entrée des « commandes de contrôle » disponibles, ce qui fournit un programme plus flexible. Or, si l'utilisateur s'attend à connaitre quel ENGINE il utilise (dans le cas de matériel spécialisé, cela va sans dire) alors l'application peut ne pas s'intéresser à la découverte des commandes de contrôle gérées et plus simplement préférer passer des réglages directement à l'ENGINE tels qu'ils sont fournis par l'utilisateur.

Avant d'illustrer les commandes de contrôle, il est raisonnable de mentionner ce pour quoi elles sont utilisées. De façon générale, il y a deux utilisations de commandes de contrôle ; la première consiste à fournir les détails nécessaires à l'implémentation (qui peut ne rien connaître du système hôte) pour que l'initialisation se fasse. Cela peut inclure n'importe quel chemin de pilote ou de fichier config qui doit être chargé, des adresses réseau requises, des identifiants de cartes à puce, des mots de passe pour initialiser des dispositifs sécurisés, des informations de connexion, etc. Cette classe de commandes a généralement besoin d'être passée à un ENGINE avant d'essayer de l'initialiser, c'est-à-dire avant d'appeler ENGINE_init(). L'autre classe de commande consiste en des réglages ou des opérations qui modifient certains comportements ou déclenchent la réalisation de certaines opérations, et ces commandes peuvent fonctionner avant ou après ENGINE_init(), ou dans certains cas avant et après. L'implémentation d'ENGINE doit fournir des indications de cela dans la description des commandes de contrôle intégrées et/ou dans la documentation externe finale.

Envoyer des commandes de contrôle à un ENGINE

Illustrons cela grâce à un exemple : une fonction pour laquelle l'appelant fournit le nom de l'ENGINE qu'il veut utiliser, une table de paires de chaînes de caractères à utiliser avant l'initialisation, et d'autres tables après l'initialisation. Notez que les paires de chaînes de caractères utilisées pour les commandes de contrôles consistent en un « nom » de commande suivit du « paramètre » de la commande — le paramètre pourrait être NULL dans certains cas mais le nom ne peut pas l'être. Cette fonction devrait initialiser l'ENGINE (en envoyant les « pré » commandes avant et les « post » commandes après) et le régler comme défaut pour tout sauf RAND, puis renvoyer un booléen pour un succès ou un échec.

 int generic_load_engine_fn(const char *engine_id,
                            const char **pre_cmds, int pre_num,
                            const char **post_cmds, int post_num)
 {
     ENGINE *e = ENGINE_by_id(engine_id);
     if(!e) return 0;
     while(pre_num--) {
         if(!ENGINE_ctrl_cmd_string(e, pre_cmds[0], pre_cmds[1], 0)) {
             fprintf(stderr, "Failed command (%s - %s:%s)\n", engine_id,
                 pre_cmds[0], pre_cmds[1] ? S<pre_cmds[1] :> "(NULL)");
             ENGINE_free(e);
             return 0;
         }
         pre_cmds += 2;
     }
     if(!ENGINE_init(e)) {
         fprintf(stderr, "Failed initialisation\n");
         ENGINE_free(e);
         return 0;
     }
     /* ENGINE_init() a renvoyé une référence fonctionnelle, il faut donc libérer la référence structurelle d'ENGINE_by_id(). */
     ENGINE_free(e);
     while(post_num--) {
         if(!ENGINE_ctrl_cmd_string(e, post_cmds[0], post_cmds[1], 0)) {
             fprintf(stderr, "Failed command (%s - %s:%s)\n", engine_id,
                 post_cmds[0], post_cmds[1] ? S<post_cmds[1] :> "(NULL)");
             ENGINE_finish(e);
             return 0;
         }
         post_cmds += 2;
     }
     ENGINE_set_default(e, ENGINE_METHOD_ALL & ~ENGINE_METHOD_RAND);
     /* Succès */
     return 1;
 }

Notez qu'ENGINE_ctrl_cmd_string() accepte un argument booléen qui peut assouplir les sémantiques de la fonction — s'il est différent de 0, elle ne renverra un échec que si l'ENGINE prend en charge le nom de la commande mais a échoué lors de son exécution ; si l'ENGINE ne prend pas en charge le nom de la commande, elle renverra simplement succès sans rien faire. Dans ce cas, on suppose que l'utilisateur apporte des commandes spécifiques à l'ENGINE donné, on règle donc cela à FALSE.

Découverte de commandes de contrôle gérées

Il est possible de découvrir les noms, id numériques, descriptions et paramètres d'entrée des commandes de contrôle prises en charge par un ENGINE au moment de l'exécution en utilisant une référence structurelle. Notez que certaines commandes de contrôle définies dans OpenSSL intercepteront et géreront ces appels au nom d'ENGINE, c'est-à-dire le gestionnaire ctrl() d'ENGINE n'est pas utilisé pour les commandes de contrôle. openssl/engine.h définit un index, ENGINE_CMD_BASE, à partir duquel toutes les commandes de contrôle implémentées par ENGINE doivent être numérotées. Toute valeur de commande plus basse que ce symbole est considérée comme « générique » et est gérée directement par les routines noyau d'OpenSSL.

C'est en utilisant ces contrôles « essentiels » que l'on peut découvrir les commandes de contrôle implémentées par un ENGINE donné, plus spécifiquement ces commandes :

 #define ENGINE_HAS_CTRL_FUNCTION               10
 #define ENGINE_CTRL_GET_FIRST_CMD_TYPE         11
 #define ENGINE_CTRL_GET_NEXT_CMD_TYPE          12
 #define ENGINE_CTRL_GET_CMD_FROM_NAME          13
 #define ENGINE_CTRL_GET_NAME_LEN_FROM_CMD      14
 #define ENGINE_CTRL_GET_NAME_FROM_CMD          15
 #define ENGINE_CTRL_GET_DESC_LEN_FROM_CMD      16
 #define ENGINE_CTRL_GET_DESC_FROM_CMD          17
 #define ENGINE_CTRL_GET_CMD_FLAGS              18

Alors que ces commandes sont traitées de façon automatique par le code du framework d'OpenSSL, elles utilisent diverses propriétés exposées par chaque ENGINE pour traiter ces requêtes. Un ENGINE a 3 propriétés qu'il expose qui peuvent affecter comment celles-ci se comportent ; il peut fournir un contrôleur ctrl(), il peut spécifier ENGINE_FLAGS_MANUAL_CMD_CTRL dans les drapeaux de l'ENGINE, et il peut exposer un tableau de descriptions de commandes de contrôle. Si un ENGINE spécifie le drapeau ENGINE_FLAGS_MANUAL_CMD_CTRL, alors les commandes essentielles de contrôle seront simplement passées directement au gestionnaire ctrl() de l'ENGINE (et de ce fait, il doit y en avoir un de fourni), c'est donc le rôle de l'ENGINE de répondre à ces commandes « découvertes ». Si un drapeau n'est pas déterminé, alors le code du framework d'OpenSSL fonctionnera avec les règles suivantes :

 si aucun gestionnaire ctrl() n'est S<fourni ;>
     ENGINE_HAS_CTRL_FUNCTION renvoie FALSE (zero),
     toutes les autres commandes échouent.
 si un gestionnaire ctrl() a été fourni mais aucun tableau de commandes de contrôle n'a été S<fourni ;>
     ENGINE_HAS_CTRL_FUNCTION renvoie TRUE,
     toutes les autres commandes échouent.
 si un gestionnaire ctrl() et un tableau de commandes ont été S<fournis ;>
     ENGINE_HAS_CTRL_FUNCTION renvoie TRUE,
     toutes les autres commandes continuent leurs traitements ...

Si le tableau de commandes de contrôle d'ENGINE est vide alors toutes les autres commandes échoueront, sinon ENGINE_CTRL_GET_FIRST_CMD_TYPE renvoie l'identifiant de la première commande prise en charge par l'ENGINE, ENGINE_GET_NEXT_CMD_TYPE prend l'identifiant d'une commande prise en charge par l'ENGINE et renvoie le prochain identifiant de commande, ou échoue s'il n'y en a plus, ENGINE_CMD_FROM_NAME prend un nom sous forme de chaîne de caractères pour une commande et renvoie l'identifiant correspondant ou échoue si la commande correspondante n'existe pas. Les commandes restantes prennent un identifiant de commande et renvoient les propriétés de la commande correspondante. Toutes, sauf ENGINE_CTRL_GET_FLAGS, renvoient la longueur de la chaîne de caractères du nom de la commande ou de la description, ou remplissent un tampon de caractères fourni avec la copie du nom ou de la description de la commande. ENGINE_CTRL_GET_FLAGS renvoie un masque OR bit à bit ayant les valeurs possibles suivantes :

 #define ENGINE_CMD_FLAG_NUMERIC                (unsigned int)0x0001
 #define ENGINE_CMD_FLAG_STRING                 (unsigned int)0x0002
 #define ENGINE_CMD_FLAG_NO_INPUT               (unsigned int)0x0004
 #define ENGINE_CMD_FLAG_INTERNAL               (unsigned int)0x0008

Si le drapeau ENGINE_CMD_FLAG_INTERNAL est défini, alors n'importe quels autres drapeaux sont purement informationnels pour l'appelant — ce drapeau empêchera la commande d'être exécutée par les fonctions haut niveau d'un ENGINE comme ENGINE_ctrl_cmd_string(). Les commandes « INTERNES » ne sont pas supposées être exposées à des configurations basées sur du texte par les applications, administrations, utilisateurs, etc. Celles-ci peuvent gérer des opérations arbitraires à l’aide d'ENGINE_ctrl(), y compris le passage de ou vers les données des commandes de contrôle de n'importe quel type arbitraire. Ces commandes sont gérées dans le mécanisme de découverte pour simplement autoriser les applications à déterminer si un ENGINE gère certaines commandes spécifiques qu'il veut utiliser (par exemple : l'application « foo » veut faire une requête à différents ENGINE pour voir s'ils implémentent « FOO_GET_VENDOR_LOGO_GIF », et ENGINE peut de ce fait décider s'il prend en charge ou non cette extension spécifique à « foo »).

Développements futurs

L'API d'ENGINE et sa structure architecturale interne sont en cours de révision. Il est prévu d'avoir de nouvelles modifications dans la version 0.9.8 pour une prise en charge du chargement transparent d'ENGINE « dynamiques » (implémentés comme bibliothèques partagées autonomes). Cela devrait autoriser de fournir des implémentations ENGINE indépendamment des bibliothèques d'OpenSSL ou des applications basées sur OpenSSL, et supprimera aussi toute obligation pour les applications d'utiliser explicitement l'ENGINE « dynamique » pour se lier à des implémentations de bibliothèques partagées.

VOIR AUSSI

rsa(3), dsa(3), dh(3), rand(3)

TRADUCTION

La traduction de cette page de manuel est maintenue par les membres de la liste <debian-l10n-french AT lists DOT debian DOT org>. Veuillez signaler toute erreur de traduction par un rapport de bogue sur le paquet manpages-fr-extra.

2015-12-31 1.0.2a 1.0.2c