Bonjour à tous,

        Désolé de venir avec un problème pas tout à fait Debian. J'essaye de
m'inscrire sur les listes de diffusion OpenSSL depuis plusieurs jours
sans aucun retour. Je pense que des lecteurs ont déjà été confrontés au
problème.

        J'utilise OpenSSL (3.3.1) pour chiffrer des messages et les déchiffrer.
J'observe un comportement que je ne comprends pas. Je n'utilise qu'un
chiffrement de type AES256-ECB (donc le chiffrement d'un bloc ne dépend
que du bloc en question. Je sais, ce n'est pas bien, mais le
périphérique en face est un microcontrôleur qui cause en 4G et qui
possède très peu de mémoire).

        Lorsque mon périphérique parle à mon serveur Debian, mon programme
plante méchamment lors de l'appel à la fonction EVP_CipherFinal_ex().

        Que fais-je ?

        Je crée un contexte, je l'initialise avec la clef et tout ce qui va
bien puis je chiffre :

    if ((contexte = EVP_CIPHER_CTX_new()) == NULL)
    {
        return(NULL);
    }

    EVP_CIPHER_CTX_reset(contexte);

    longueur_bloc_de_chiffrement =
        EVP_CIPHER_block_size(type_chiffrement);

    if (EVP_CipherInit_ex(contexte, type_chiffrement, NULL, clef,
            vecteur_initialisation, (encodage == d_vrai) ? 1 : 0) != 1)
    {
        EVP_CIPHER_CTX_free(contexte);
        return(NULL);
    }

    nombre_blocs = longueur_message / longueur_bloc_de_chiffrement;

    if ((longueur_message % longueur_bloc_de_chiffrement) != 0)
    {
        nombre_blocs++;
    }

    (*longueur_message_chiffre) = nombre_blocs *
        longueur_bloc_de_chiffrement;

printf("longueur_message_chiffre = %lld\n", (*longueur_message_chiffre));

    // On prévoit une zone de garde pour EVP_CipherFinal_ex() en
    // espérant
    // qu'il ne faille pas plus qu'une longueur de bloc de chiffrement.
    // Méchant hack en raison d'un comportement étrange de
    // EVP_CipherFinal_ex().
    if ((message_chiffre = malloc(((size_t) ((*longueur_message_chiffre)
            + longueur_bloc_de_chiffrement)) *
            sizeof(unsigned char))) == NULL)
    {
        EVP_CIPHER_CTX_free(contexte);
        return(NULL);
    }

printf("longueur_message = %d\n", (int) longueur_message);

    if (EVP_CipherUpdate(contexte, message_chiffre, &longueur_message_1,
            message, (int) longueur_message) != 1)
    {
        free(message_chiffre);
        EVP_CIPHER_CTX_free(contexte);
        return(NULL);
    }

printf("longueur_message_1 = %d\n", longueur_message_1);

    if (EVP_CipherFinal_ex(contexte, message_chiffre +
            longueur_message_1,
            &longueur_message_2) != 1)
    {
        free(message_chiffre);
        EVP_CIPHER_CTX_free(contexte);
        return(NULL);
    }

printf("longueur_message_2 = %d\n", longueur_message_2);

    (*longueur_message_chiffre) = longueur_message_1 +
            longueur_message_2;

    // Mise à jour du vecteur d'initialisation

    EVP_CIPHER_CTX_get_updated_iv(contexte, vecteur_initialisation,
            (size_t) EVP_CIPHER_iv_length(type_chiffrement));

    EVP_CIPHER_CTX_free(contexte);

        Cette routine est appelée pour chiffrer et pour déchiffrer. Je ne mets
pas tout le code, ça n'a pas beaucoup d'intérêt. Maintenant, mon problème.

        Le code AES256 utilise des blocs de 16 octets. Si je chiffre un message
de 15 octets, j'obtiens un message chiffré de 16 octets. Normal.

longueur_message_chiffre = 16
longueur_message = 15
longueur_message_1 = 0
longueur_message_2 = 16

        Ainsi, EVP_CipherUpdate() ne fait rien (aucun bloc entier) et le
chiffrement est effectué par EVP_CipherFinal_ex().

        Si maintenant je cherche à chiffrer un message de 16 octets, voici ce
qui sort :

longueur_message_chiffre = 16
longueur_message = 16
longueur_message_1 = 16
longueur_message_2 = 16
=> longueur_message_chiffre donne la longueur nécessaire au chiffrement
du message. La longueur effectivement renvoyée est
longueur_message_1+longueur_message_2 soit ici 32 !

        Si je comprends bien, EVP_CipherUpdate() chiffre un bloc de 16 octets.
Mais pourquoi EVP_CipherFinal_ex() rajoute-t-il un autre bloc de 16
octets ? J'obtiens un message de 32 octets alors que seuls les 16
premiers sont signifiants.

        Dans mon cas, j'obtiens :
"?r\xF1\xF0-\x89\x83]\xD3\xEE\xF0bj\xE2\xB7\x9E\xE34\xF2\xD2f
\xF6w\x02\x14\x0DbS\xD5\x01\x1A"

alors que seul

"?r\xF1\xF0-\x89\x83]\xD3\xEE\xF0bj\xE2\xB7\x9E" contient le message
chiffré (le formalisme est le suivant : les caractères affichables sont
directement affichés, les autres sont \x + octet en hexadécimal).

        Le problème est aussi que je suis capable de déchiffrer le message
complet "?r\xF1\xF0-\x89\x83]\xD3\xEE\xF0bj\xE2\xB7\x9E\xE34\xF2\xD2f
\xF6w\x02\x14\x0DbS\xD5\x01\x1A"
en une chaîne de 16 octets (qui est bien le message envoyé). En
revanche, si je tente le déchiffrement des seuls 16 premiers octets,
EVP_CipherFinal_ex() renvoie une erreur.

        Je ne comprends pas mon erreur. Quelqu'un a-t-il déjà été confronté à
ce problème ? J'ai trouvé des tas d'exemples sur internet, mais, à
chaque fois, EVP_CipherFinal_ex() est appelé.

        Je prends tout avis, tout pointeur sur une documentation compréhensible
(celle d'OpenSSL est un peu... touffue).

        Merci de votre lecture,

        JKB

Attachment: signature.asc
Description: OpenPGP digital signature

Répondre à