Erreur dans mon code SHA-1

Résolu
ndubien Messages postés 557 Date d'inscription dimanche 25 septembre 2005 Statut Membre Dernière intervention 10 mai 2014 - 2 mars 2011 à 19:24
cptpingu Messages postés 3837 Date d'inscription dimanche 12 décembre 2004 Statut Modérateur Dernière intervention 28 mars 2023 - 5 mars 2011 à 15:56
Bonjour,

Je viens d'essayer de développer une application retournant la valeur hachée (SHA-1) d'un fichier, cependant malgré de nombreuses relectures du code et des documents disponibles sur internet relatifs à ce sujet, je ne parviens pas à trouver l'erreur.

Voici le code (il n'est pas optimisé, j'avais juste pour objectif de reprendre le c/c++ en créant un petit programme avec les connaissances qui me restaient à propos du langage):
string SHA1( string nomFichier ) {

    /* Obtention des Mi (512 bits soit 64 octets) */
    /*
     - 1 est ajouté à la fin de x
     - On ajoute des 0 à la fin de x pour que ||x|| =  512n − 64
     - ||x|| est écrite en base 2 sur 64 bits, qui sont rajoutés à la fin du mot ci-dessus
    */

    ifstream monFlux;
    monFlux.open( nomFichier.c_str() );
    if( monFlux ) {
        monFlux.seekg (0, ios::end);
        unsigned int length = monFlux.tellg(); /* ATTENTION: au-delà de 2^32 :'( */
        monFlux.seekg (0, ios::beg);

        /* 512 bits = 64 octets */
        /*  64 bits =  8 octets */
        int nbLectures = ( length+1 +8 )/64;
        if( ( length+1 +8 )%64 > 0 ) ++nbLectures;

        unsigned int h0 = 0x67452301; /* unsigned int: 32 bits */
        unsigned int h1 = 0xEFCDAB89; /* http://www.commentcamarche.net/contents/cpp/cpptype.php3 */
        unsigned int h2 = 0x98BADCFE;
        unsigned int h3 = 0x10325476;
        unsigned int h4 = 0xC3D2E1F0;

        for( int i=0 ; i!=nbLectures ; ++i ) {

            int tailleLecture = 64; /* 64 octets */
            if( i nbLectures-1 ) tailleLecture length%64;

            char* buffer = new char[tailleLecture];
            monFlux.read( buffer, tailleLecture );

            unsigned int M[16]; /* 512 bits / 32 bits */
            for( int j=0 ; j!=16 ; ++j ) { M[j] = 0; }
            for( int t=0 ; t!=64 ; ++t ) {
                if( t < tailleLecture ) {
                    unsigned char tmp = *(buffer+t);
                    M[ t/4 ] += ( tmp << 8*( 3 - (t%4) ) );
                } else if( t == tailleLecture ) /* On ajoute le char: % 1000 0000 ie 128 */
                    M[ t/4 ] += ( 128 << 8*( 3 - (t%4) ) );
                /* length: "64" bits soit 2*32, on les ajoute après */
                /* else rien, on ajoute des 0 */
                if( i == nbLectures-1 ) {
                    /*M[ 14 ] = 0; length fait en réalité 32 bits */
                    M[15] = length;
                }
            }
            free( buffer );

            /* Obtient les Wt */
            unsigned int W[80];
            for( int t=0 ; t!=16 ; ++t ) { W[t] = M[t]; }
            for( int t=16 ; t!=80 ; ++t ) { W[t] = ROL32( W[t-3] xor W[t-8] xor W[t-14] xor W[t-16], 1 );  }

            /* A, B, C, D, E */
            unsigned int A = h0;
            unsigned int B = h1;
            unsigned int C = h2;
            unsigned int D = h3;
            unsigned int E = h4;

            /* Boucle sur t... */
            for( int t=0 ; t!=80 ; ++t ) {
                /*
                T = S5(A) + (ft(B,C,D) + Kt) + E + Wt
                E = D
                D = C
                C = S30(B)
                B = A
                A = T
                */
                unsigned int tmp;
                /* 19/ (B &#8743; C) or (¬B &#8743; D) *//* 39/ B xor C xor D *//* 59/ (B &#8743; C) or (B &#8743; D) or (C &#8743; D) *//* B xor C xor D */
                if( t <19 )      tmp ( (B & C) | ((~B) & D) )        + 0x5A827999;
                else if( t <39 ) tmp ( B ^ C ^ D )                   + 0x6ED9EBA1;
                else if( t <59 ) tmp ( (B & C) | (B & D) | (C & D) ) + 0x8F1BBCDC;
                else               tmp = ( B ^ C ^ D )                   + 0xCA62C1D6;

                unsigned int T = ROL32(A,5) + tmp + E + W[t];
                E = D;
                D = C;
                C = ROL32(B,30);
                B = A;
                A = T;
            }

            /* Ajouter A à H0, B à H1, C à H2, D à H3 et E à H4 */
            h0 += A;
            h1 += B;
            h2 += C;
            h3 += D;
            h4 += E;
        }

        monFlux.close();

        return hexa_of_valeurhachee( (unsigned int[]){h0,h1,h2,h3,h4} );
    } else return 0;
}


Le problème est qu'elle ne renvoie pas la bonne valeur hachée:
* Pour le fichier "test":
retour = 9b0decbd3c093f369d6d2462d784ad49a9ff4698
au lieu de = a94a8fe5ccb19ba61c4c0873d391e987982fbbd3
* Pour le fichier "": le retour est correct da39a3ee5e6b4b0d3255bfef95601890afd80709

Merci d'avance et à bientôt...

Nico
PS: Articles concernant le hachage SHA-1:
http://en.wikipedia.org/wiki/SHA-1
http://fr.wikipedia.org/wiki/Sp%C3%A9cifications_SHA-1
http://www.sinfocol.org/herramientas/hashes.php

13 réponses

cs_Lucky92 Messages postés 180 Date d'inscription mercredi 22 décembre 2004 Statut Membre Dernière intervention 16 août 2012 2
3 mars 2011 à 19:32
Bingo !

Remplace simplement :
 M[15] = length;


Par :
 M[15] = length * 8;


Dans l'algo la taille à hacher est exprimée en nombre de bits et non en nombre de bytes.

good bytes:)

PS : au temps pour moi pour la fausse piste de l'endianness.
3
cs_Lucky92 Messages postés 180 Date d'inscription mercredi 22 décembre 2004 Statut Membre Dernière intervention 16 août 2012 2
2 mars 2011 à 22:39
Salut,

Je ne suis pas sûr de mon coup car j'ai survolé très vite ton problème et je n'ai pas eu le temps de tester, mais dans l'article wikipédia que tu cites, il est dit que les constantes sont fournies en big endian. Or, si tu utilises un PC, il y a forte chance que tu sois en little endian ; tu peux essayer de permuter les octets des constantes hexa.

@+.
0
ndubien Messages postés 557 Date d'inscription dimanche 25 septembre 2005 Statut Membre Dernière intervention 10 mai 2014 4
2 mars 2011 à 23:47
Bonjour,

Le projet a en effet été compilé sous Windows (avec CodeBlocks), mais je souhaitais également pouvoir le recompiler sous Linux (Ubuntu avec CodeBlocks (gcc)).

Comment faut-il procéder pour passer de big endian à little endian ?
Pourrais-je conserver le même code pour Linux ou faudra-t'il changer ceci d'une version à l'autre ?

Merci d'avance et à bientôt...
Nico
0
cs_Lucky92 Messages postés 180 Date d'inscription mercredi 22 décembre 2004 Statut Membre Dernière intervention 16 août 2012 2
3 mars 2011 à 06:59
Salut,

L'endianness ne dépend ni de l'OS ni du compilateur mais de l'architecture matérielle. Elle correspond à la représentation des données en mémoire, et dans le monde PC, les données sont représentées en little endian.
Il suffit de permuter les octets dans les constantes comme ceci :

 unsigned int h0 = 0x01234567; //0x67452301; 
 unsigned int h1 = 0x89ABCDEF; //0xEFCDAB89; 
 unsigned int h2 = 0xFEDCBA98; //0x98BADCFE;
 unsigned int h3 = 0x76543210; //0x10325476;
 unsigned int h4 = 0xF0E1D2C3; //0xC3D2E1F0;


 if( t <19 )      tmp ( (B & C) | ((~B) & D) )        + 0x9979825A; //+ 0x5A827999;
 else if( t <39 ) tmp ( B ^ C ^ D )                   + 0xA1EBD96E; //+ 0x6ED9EBA1;
 else if( t <59 ) tmp ( (B & C) | (B & D) | (C & D) ) + 0xDCBC1B8F; //+ 0x8F1BBCDC;
 else               tmp = ( B ^ C ^ D )                   + 0xD6C162CA; //+ 0xCA62C1D6;


@+
0

Vous n’avez pas trouvé la réponse que vous recherchez ?

Posez votre question
ndubien Messages postés 557 Date d'inscription dimanche 25 septembre 2005 Statut Membre Dernière intervention 10 mai 2014 4
3 mars 2011 à 11:13
Bonjour,

Je viens d'essayer les modifications que vous m'avez suggéré, cependant le problème persiste, je n'obtiens toujours pas le bon résultat.

Merci d'avance et à bientôt... Nico
0
cs_Lucky92 Messages postés 180 Date d'inscription mercredi 22 décembre 2004 Statut Membre Dernière intervention 16 août 2012 2
3 mars 2011 à 12:55
Peux-tu poster la macro ROL32 ?
0
cs_Lucky92 Messages postés 180 Date d'inscription mercredi 22 décembre 2004 Statut Membre Dernière intervention 16 août 2012 2
3 mars 2011 à 13:06
..et l'opérateur ou la macro xor aussi.
0
cs_Lucky92 Messages postés 180 Date d'inscription mercredi 22 décembre 2004 Statut Membre Dernière intervention 16 août 2012 2
3 mars 2011 à 13:09
...et la fonction hexa_of_valeurhachee aussi.
0
ndubien Messages postés 557 Date d'inscription dimanche 25 septembre 2005 Statut Membre Dernière intervention 10 mai 2014 4
3 mars 2011 à 16:05
Bonjour,

Voici les fonctions que vous m'avez demandé:
string hexa_of_uint( unsigned int entier ) {
    string hexa = "";
    for( int i=0 ; i!=8 ; ++i ) { /* entier: 32 bits, hexa: 4bits */
        switch( entier%16 ) {
            case 0:  hexa = "0" + hexa; break;
            case 1:  hexa = "1" + hexa; break;
            case 2:  hexa = "2" + hexa; break;
            case 3:  hexa = "3" + hexa; break;
            case 4:  hexa = "4" + hexa; break;
            case 5:  hexa = "5" + hexa; break;
            case 6:  hexa = "6" + hexa; break;
            case 7:  hexa = "7" + hexa; break;
            case 8:  hexa = "8" + hexa; break;
            case 9:  hexa = "9" + hexa; break;
            case 10: hexa = "a" + hexa; break;
            case 11: hexa = "b" + hexa; break;
            case 12: hexa = "c" + hexa; break;
            case 13: hexa = "d" + hexa; break;
            case 14: hexa = "e" + hexa; break;
            case 15: hexa = "f" + hexa; break;
            default: hexa = "*" + hexa; break;
        }
        entier /= 16;
    }
    return hexa;
}

string hexa_of_valeurhachee( unsigned int* valeurhacheee ) {
    string hexa = "";
    for( int i=0 ; i!= 5 ; ++i ) { hexa += hexa_of_uint( *(valeurhacheee+i) ); }
    return hexa;
}

et ROL32:
#define ROL32(x,b) (((x) << (b)) | ((x) >> (32 - (b))))


pour les fonctions xor, or et and j'utilise les fonctions de bases définies par le langage (xor:uint*uint->uint...).

Merci d'avance et à bientôt...
Nico
0
ndubien Messages postés 557 Date d'inscription dimanche 25 septembre 2005 Statut Membre Dernière intervention 10 mai 2014 4
3 mars 2011 à 20:54
Bonjour,

Je tenais à vous remercier pour votre aide, ça fonctionne parfaitement sur les deux exemples essayé, j'en essaierai d'autres, mais ça doit être correct. La prochaine fois j'essaierai de mieux lire et pour l'endianness et pour les bits.

Dernière question: j'utilise unsigned int pour avoir des variables de 32 bits, mais existe-t-il un type du genre uint32 (pour les opérations) et uint64 (pour length) afin d'être sûr d'avoir des variables de 32 bits (resp. 64 bits). Car sur un processeur 16 bits unsigned int est sur 16 bits, 32 bits sur un 32 bits donc 64 bits sur un 64 bits ?

Merci d'avance et à bientôt
Nico

Encore merci pour votre réponse :)
0
cs_Lucky92 Messages postés 180 Date d'inscription mercredi 22 décembre 2004 Statut Membre Dernière intervention 16 août 2012 2
3 mars 2011 à 22:45
Bonsoir,

"long" et "unsigned long" sont encodés sur 32 bits.
"long long" et "unsigned long long" sont encodés sur 64 bits.
Tu peux introduire des typedef à ta guise pour alléger le code.

@+
0
ndubien Messages postés 557 Date d'inscription dimanche 25 septembre 2005 Statut Membre Dernière intervention 10 mai 2014 4
4 mars 2011 à 01:22
Merci et à bientôt ;)
0
cptpingu Messages postés 3837 Date d'inscription dimanche 12 décembre 2004 Statut Modérateur Dernière intervention 28 mars 2023 123
5 mars 2011 à 15:56
Dans stdint.h tu as les types uint32_t, int32_t, uint64_t, int64_t, etc...

________________________________________________________________________
Historique de mes créations, et quelques articles:
[ http://0217021.free.fr/portfolio http://0217021.free.fr/portfolio]
Merci d'utiliser Réponse acceptée si un post répond à votre question
0
Rejoignez-nous