IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

C++ Discussion :

Problèmes pour implémenter SHA256


Sujet :

C++

  1. #1
    Expert confirmé

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2007
    Messages
    1 895
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Septembre 2007
    Messages : 1 895
    Points : 4 551
    Points
    4 551
    Par défaut Problèmes pour implémenter SHA256
    Vu qu'il n'y a pas de section "sécurité" en dehors de tout contexte sur ce forum, je poste ma question ici.

    Histoire de m'amuser, et parce que je pensais que c'était facile, je me suis dit "tiens, et pourquoi ne pas implémenter cet algorithme de hashage, histoire d'être libéré de la licence ?" (je n'ai pas trouvé d'implémentation qui soit compatible avec mes exigences de licence).

    Et je m'y suis mis. Et le résultat dépasse mes espérance : les hash obtenus sont complètement différents de ce que je devrais normalement obtenir, et je suis incapable de savoir à quel niveau ça foire. Debugger ne sert pas à grand chose, tant la complexité des formules rends toute interprétation impossible.

    Peut-être pourrez-vous pointer ici ou là un problème quelconque. A noter que l'algorithme, tel que décrit dans les documents officiels est censé fonctionner sur du big endian - et bien évidemment, mon implémentation est en little endian.

    Voici le code C++
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
     
    #include <cstring>
    #include <tr1/cstdint>
    #include "security_sha_digest.h" // voir plus bas
     
    namespace ekogen { namespace security {
     
    	namespace 
    	{
     
    		typedef std::tr1::uint32_t uint32_t;
    		typedef std::tr1::uint64_t uint64_t;
     
    		inline unsigned int rotleft(uint32_t value, int shift) 
    		{
    			if ((shift &= 31) == 0) return value;
    			return (value << shift) | (value >> (32 - shift));
    		}
     
    		inline unsigned int rotright(uint32_t value, int shift) 
    		{
    			if ((shift &= 31) == 0) return value;
    			return (value >> shift) | (value << (32 - shift));
    		}
     
    	}
     
    #define BSWAP32(x) 		(((uint32_t)(x) << 24) | \
    						 ((uint32_t)(x) >> 24) | \
    						 (((uint32_t)(x) << 8) & 0xff0000L) | \
    						 (((uint32_t)(x) >> 8) & 0xff00L))
     
     
    #define	BSWAP64(x)      (((uint64_t)(x) << 56) | \
                             (((uint64_t)(x) << 40) & 0xff000000000000ULL) | \
                             (((uint64_t)(x) << 24) & 0xff0000000000ULL) | \
                             (((uint64_t)(x) << 8)  & 0xff00000000ULL) | \
                             (((uint64_t)(x) >> 8)  & 0xff000000ULL) | \
                             (((uint64_t)(x) >> 24) & 0xff0000ULL) | \
                             (((uint64_t)(x) >> 40) & 0xff00ULL) | \
                             ((uint64_t)(x)  >> 56))
     
    	// SHA-256 constants
    	namespace constants {
     
    		const uint32_t h0 = (0x6a09e667);
    		const uint32_t h1 = (0xbb67ae85);
    		const uint32_t h2 = (0x3c6ef372);
    		const uint32_t h3 = (0xa54ff53a);
    		const uint32_t h4 = (0x510e527f);
    		const uint32_t h5 = (0x9b05688c);
    		const uint32_t h6 = (0x1f83d9ab);
    		const uint32_t h7 = (0x5be0cd19);
     
    		const uint32_t k[64] = 
    		{
    			(0x428a2f98), (0x71374491), (0xb5c0fbcf), (0xe9b5dba5), 
    			(0x3956c25b), (0x59f111f1), (0x923f82a4), (0xab1c5ed5),
    			(0xd807aa98), (0x12835b01), (0x243185be), (0x550c7dc3), 
    			(0x72be5d74), (0x80deb1fe), (0x9bdc06a7), (0xc19bf174),
    			(0xe49b69c1), (0xefbe4786), (0x0fc19dc6), (0x240ca1cc), 
    			(0x2de92c6f), (0x4a7484aa), (0x5cb0a9dc), (0x76f988da),
    			(0x983e5152), (0xa831c66d), (0xb00327c8), (0xbf597fc7), 
    			(0xc6e00bf3), (0xd5a79147), (0x06ca6351), (0x14292967),
    			(0x27b70a85), (0x2e1b2138), (0x4d2c6dfc), (0x53380d13), 
    			(0x650a7354), (0x766a0abb), (0x81c2c92e), (0x92722c85),
    			(0xa2bfe8a1), (0xa81a664b), (0xc24b8b70), (0xc76c51a3), 
    			(0xd192e819), (0xd6990624), (0xf40e3585), (0x106aa070),
    			(0x19a4c116), (0x1e376c08), (0x2748774c), (0x34b0bcb5), 
    			(0x391c0cb3), (0x4ed8aa4a), (0x5b9cca4f), (0x682e6ff3),
    			(0x748f82ee), (0x78a5636f), (0x84c87814), (0x8cc70208), 
    			(0x90befffa), (0xa4506ceb), (0xbef9a3f7), (0xc67178f2)
    		};
    	}
     
    	namespace details {
     
    		inline uint32_t ch(uint32_t x, uint32_t y, uint32_t z)
    		{
    			return (x & y) ^ (~x & z);
    		}
     
    		inline uint32_t maj(uint32_t x, uint32_t y, uint32_t z)
    		{
    			return (x & y) ^ (x & z) ^ (y & z);
    		}
     
    		inline uint32_t sigma0(uint32_t x)
    		{
    			return rotright(x, 2) ^ rotright(x, 13) ^ rotright(x, 22);
    		}
     
    		inline uint32_t sigma1(uint32_t x)
    		{
    			return rotright(x, 6) ^ rotright(x, 11) ^ rotright(x, 25);
    		}
     
    		inline uint32_t s0(uint32_t x)
    		{
    			return rotright(x, 7) ^ rotright(x, 18) ^ (x >> 3);
    		}
     
    		inline uint32_t s1(uint32_t x)
    		{
    			return rotright(x, 17) ^ rotright(x, 19) ^ (x >> 10);
    		}
     
    		inline void cycle(const uint32_t* w, std::size_t round, 
    						  uint32_t& a, uint32_t& b, uint32_t& c, uint32_t& d, 
    						  uint32_t& e, uint32_t& f, uint32_t& g, uint32_t& h)
    		{
    			uint32_t t1 = h + sigma1(e) + ch(e,f,g) + constants::k[round] + w[round];
    			uint32_t t2 = sigma0(a) + maj(a,b,c);
    			h = g;
    			g = f;
    			f = e;
    			e = d + t1;
    			d = c;
    			c = b;
    			b = a;
    			a = t1 + t2;
    		}
     
    		void do_rounds(const uint32_t*w, uint32_t* hash, std::size_t round_count)
    		{
    			for (std::size_t round=0; round < round_count; ++round)
    			{
    				uint32_t a = hash[0];
    				uint32_t b = hash[1];
    				uint32_t c = hash[2];
    				uint32_t d = hash[3];
    				uint32_t e = hash[4];
    				uint32_t f = hash[5];
    				uint32_t g = hash[6];
    				uint32_t h = hash[7];
     
    				details::cycle(w, round, a, b, c, d, e, f, g, h);
     
    				hash[0] += a;
    				hash[1] += b;
    				hash[2] += c;
    				hash[3] += d;
    				hash[4] += e;
    				hash[5] += f;
    				hash[6] += g;
    				hash[7] += h;
    			}
    		}
     
    		void prepare(const unsigned char* in, std::size_t length, std::size_t full_length, uint32_t* w)
    		{
    			if (length < 64)
    			{
    				memcpy(w, in, length);
     
    				unsigned char* wc = reinterpret_cast<unsigned char*>(w);
    				wc[length] = 0x80;
    				for (std::size_t i=length; i<56; ++i)
    				{
    					wc[i] = 0;
    				}
    				for (std::size_t i=0; i<14; ++i)
    				{
    					w[i] = BSWAP32(w[i]);
    				}
    				uint64_t* w64 = reinterpret_cast<uint64_t*>(w);
    				w64[7] = BSWAP64(full_length * 8);
    			}
    			else
    			{
    				const uint32_t* in32 = reinterpret_cast<const uint32_t*>(in);
    				for (std::size_t i=0; i<16; ++i)
    				{
    					w[i] = BSWAP32(in32[i]);
    				}
    			}
     
    			for (std::size_t r = 16; r < 64; ++r)
    			{
    				w[r] = s1(w[r-2]) + w[r-7] + s0(w[r-15]) + w[r-16];
    			}
    		}
    	}
     
    	void sha256(const unsigned char* in, std::size_t length, std::vector<unsigned char>& output)
    	{
    		const std::size_t chunk_size = 512 / 8;
    		const std::size_t round_count = 64;
     
    		// 1. prepare output
    		std::vector<unsigned char>(32).swap(output);
     
    		// 2. initialize
    		uint32_t hash[8] = 
    		{ 
    			constants::h0, 
    			constants::h1, 
    			constants::h2, 
    			constants::h3, 
    			constants::h4, 
    			constants::h5, 
    			constants::h6, 
    			constants::h7 
    		};
     
    		uint32_t w[round_count] = { 0 };
    		std::size_t chunk;
     
    		// 3. first part of the algorithm: all chunks except the last one
    		for (chunk = 0; chunk + chunk_size < length; chunk += chunk_size)
    		{
    			details::prepare(in + chunk, chunk_size, length, w);
     
    			details::do_rounds(w, hash, round_count);
    		}
     
    		// final round
    		details::prepare(in + chunk, length - chunk_size, length, w);
    		details::do_rounds(w, hash, round_count);
     
    		unsigned char* huc = reinterpret_cast<unsigned char*>(hash);
    		std::copy(huc, huc + sizeof(hash), output.begin());
    	}
     
    } }
    Le header security_sha_digest.h (histoire de vous amuser un peu)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
     
    #ifndef security_sha256_h
    #define security_sha256_h
     
    #include <iterator>
    #include <algorithm>
    #include <vector>
     
    namespace ekogen { namespace security {
     
    	void sha256(const unsigned char* in, std::size_t length, std::vector<unsigned char>& output);
     
    	namespace details {
     
     
    		template <class InIt, class IteratorTag>
    		struct sha256_helper
    		{
    			static void execute(InIt first, InIt last, std::vector<unsigned char>& output, const IteratorTag&)
    			{
    				typedef typename std::iterator_traits<InIt>::value_type value_type;
     
    				std::vector<value_type> v(first, last);
     
    				std::size_t byte_count = v.size() * sizeof(value_type);
    				unsigned char* from = reinterpret_cast<unsigned char*>(&v.front());
     
    				sha256(from, byte_count, output); 
    			}
    		};
     
    		template <class InIt>
    		struct sha256_helper<InIt, std::random_access_iterator_tag>
    		{
    			static void execute(InIt first, InIt last, std::vector<unsigned char>& output, const std::random_access_iterator_tag&)
    			{
    				typedef typename std::iterator_traits<InIt>::value_type value_type;
    				typedef typename std::iterator_traits<InIt>::difference_type diff_type;
     
    				diff_type count = std::distance(first, last);
    				std::size_t byte_count = count * sizeof(value_type);
    				unsigned char* from = reinterpret_cast<unsigned char*>(&(*first));
     
    				sha256(from, byte_count, output); 
    			}
    		};
     
    	}
     
    	template  <class InIt>
    	void sha256(InIt first, InIt last, std::vector<unsigned char>& output)
    	{
    		typedef typename std::iterator_traits<InIt>::iterator_category iterator_tag;
     
    		details::sha256_helper<InIt,iterator_tag>::execute(first, last, output, iterator_tag());
    	}
     
    	template  <class InIt, class Encoder>
    	void sha256(InIt first, InIt last, typename Encoder::output_type& output, Encoder encoder)
    	{
    		std::vector<unsigned char> real_output;
    		sha256(first, last, real_output);
    		encoder.encode_to(real_output, output);
    	}
     
    	template <class Encoder>
    	void sha256(const unsigned char* in, std::size_t length, typename Encoder::output_type& output, Encoder encoder)
    	{
    		std::vector<unsigned char> real_output;
    		sha256(in, length, real_output);
    		encoder.encode_to(real_output, output);
    	}
     
    } }
     
    #endif // security_sha256_h
    Je fais joujou avec les iterator_tags pour éviter toute copie dans un conteneur en entrée de la fonction dès lors que la zone mémoire [first, last[ est contigue. Au final, tout va dans la fonction définie dans le .cpp ci-dessus.

    Si vous avez une idée de problème, n'hésitez pas à m'en faire part, parce que je bloque complètement !
    [FAQ des forums][FAQ Développement 2D, 3D et Jeux][Si vous ne savez pas ou vous en êtes...]
    Essayez d'écrire clairement (c'est à dire avec des mots français complets). SMS est votre ennemi.
    Evitez les arguments inutiles - DirectMachin vs. OpenTruc ou G++ vs. Café. C'est dépassé tout ça.
    Et si vous êtes sages, vous aurez peut être vous aussi la chance de passer à la télé. Ou pas.

    Ce site contient un forum d'entraide gratuit. Il ne s'use que si l'on ne s'en sert pas.

  2. #2
    Membre chevronné
    Avatar de Goten
    Profil pro
    Inscrit en
    Juillet 2008
    Messages
    1 580
    Détails du profil
    Informations personnelles :
    Âge : 33
    Localisation : France

    Informations forums :
    Inscription : Juillet 2008
    Messages : 1 580
    Points : 2 205
    Points
    2 205
    Par défaut
    Citation Envoyé par Emmanuel Deloget Voir le message
    Histoire de m'amuser, et parce que je pensais que c'était facile,
    C'est pas trivial non plus... mais avec la RFC sous les yeux ça se fait. Après question perf ...

    je me suis dit "tiens, et pourquoi ne pas implémenter cet algorithme de hashage, histoire d'être libéré de la licence ?" (je n'ai pas trouvé d'implémentation qui soit compatible avec mes exigences de licence).
    C'est quoi tes contraintes de licences? Parce que peut être que tu sais pas bien chercher (no offense hein :p), et aussi parce que ça m'intrigue.

    J'ai pas le temps de suite mais j'y regarde ce week end, et je te dis si je trouve quelque chose . (c'est pas la première implémentation de SHA-2 que je review)
    "Hardcoded types are to generic code what magic constants are to regular code." --A. Alexandrescu

  3. #3
    Expert confirmé

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2007
    Messages
    1 895
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Septembre 2007
    Messages : 1 895
    Points : 4 551
    Points
    4 551
    Par défaut
    Mes contraintes de licence, c'est du code libre mais pas viral (donc pas GPL) ni imposant une condition inutile (genre demander a ajouter un texte de licence particulier pour pouvoir distribuer des binaires) et écrit dans un C++ correct. J'ai vu pas mal d'implémentations avec une licence compatible, mais écrit dans un C illisible.

    Il faut que je regarde la RFC. Il y a peut être des indications intéressantes.
    [FAQ des forums][FAQ Développement 2D, 3D et Jeux][Si vous ne savez pas ou vous en êtes...]
    Essayez d'écrire clairement (c'est à dire avec des mots français complets). SMS est votre ennemi.
    Evitez les arguments inutiles - DirectMachin vs. OpenTruc ou G++ vs. Café. C'est dépassé tout ça.
    Et si vous êtes sages, vous aurez peut être vous aussi la chance de passer à la télé. Ou pas.

    Ce site contient un forum d'entraide gratuit. Il ne s'use que si l'on ne s'en sert pas.

  4. #4
    Membre chevronné
    Avatar de Goten
    Profil pro
    Inscrit en
    Juillet 2008
    Messages
    1 580
    Détails du profil
    Informations personnelles :
    Âge : 33
    Localisation : France

    Informations forums :
    Inscription : Juillet 2008
    Messages : 1 580
    Points : 2 205
    Points
    2 205
    Par défaut
    (Pas sur que y'est une RFC pour SHA2 en fait, j'ai du me trouer sur ce coup là :p).
    Au final ça me torturait trop alors j'ai jeté un coup d'oeil... rien vu qui me saute aux yeux pour l'instant (sans compiler / tester).

    Ok, tu veux du C++, dommage, en C j'avais une implémentation sous license BSD à te proposer qu'était plutôt classe (et performante), mais avec les boucles déroulé à la mains (je suppose que c'est ce qui te plait pas).
    "Hardcoded types are to generic code what magic constants are to regular code." --A. Alexandrescu

  5. #5
    Membre chevronné
    Avatar de Goten
    Profil pro
    Inscrit en
    Juillet 2008
    Messages
    1 580
    Détails du profil
    Informations personnelles :
    Âge : 33
    Localisation : France

    Informations forums :
    Inscription : Juillet 2008
    Messages : 1 580
    Points : 2 205
    Points
    2 205
    Par défaut
    Bon j'ai cherché un peu plus, je trouve toujours pas. Mais à mon avis tu te tires dans le pied avec la conversion pour le little endian parce que le reste de l'algo m'a l'air valide. (fin j'ai le cerveau en dérangement ce soir).

    Ta macro pour renverser l'ordre des bit tu l'as tiens d'où? J'ai pas l'envie de la poser sur papier là mais elle me semble suspect... (fin bon, again, j'ai le cerveau en dérangement ce soir).

    Bon le truc c'est que j'ai pas de machine little endian donc forcément je peux pas test, c'est assez embêtant pour debug...
    "Hardcoded types are to generic code what magic constants are to regular code." --A. Alexandrescu

  6. #6
    Expert confirmé

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2007
    Messages
    1 895
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Septembre 2007
    Messages : 1 895
    Points : 4 551
    Points
    4 551
    Par défaut
    La macro a été vérifiée (avec du code propre et complètement différent), mais je peux toujours me tromper. La version 64 bits est issue du net (on la trouve un peu partout). La version 32 bits est calquée dessus.

    C'est un des endroits ou l'algo deviens plus obscur. Toutes les specs, y compris la RFC qui donne un code d'exemple (voui, il y en a une : la 4634) se basent sur l'idée que toutes les machines du monde sont en big endian. Ce n'est pas tout a fait vrai, et je n'ai pas de machine big endian pour tester.

    Un autre endroit tendancieux, c'est L'algorithme de padding. I'm est un peu agaçant a vérifier.

    Ce qui ne me plait pas du tout dans tous les codes que j'ai trouvé, c'est l'aspect "je sépare le code en 3 fonctions", ce qui n'a aucun sens au niveau de l'interface. Et puis c'est souvent illisible. (j'ai séparé le code autrement, mais l'ensemble va être revu dès que l'algo fonctionnera).

    Bon, ce que je vais faire des demain, c'est instrumenter une version qui fonctionne, et voir là ou je sors des différences. Étant donné que je peux me phaser a différents endroits du code (construction de w, exécution de chaque cycle) et que les formules appliquées ne changent pas, je devrait rapidement trouver le problème.

    (note to self : pourquoi est-ce que l'iPhone n'affiche pas ces pétard de barres de scroll ? C'est pas comme si c'était quelque chose de compliqué a implémenter !)
    [FAQ des forums][FAQ Développement 2D, 3D et Jeux][Si vous ne savez pas ou vous en êtes...]
    Essayez d'écrire clairement (c'est à dire avec des mots français complets). SMS est votre ennemi.
    Evitez les arguments inutiles - DirectMachin vs. OpenTruc ou G++ vs. Café. C'est dépassé tout ça.
    Et si vous êtes sages, vous aurez peut être vous aussi la chance de passer à la télé. Ou pas.

    Ce site contient un forum d'entraide gratuit. Il ne s'use que si l'on ne s'en sert pas.

  7. #7
    Expert confirmé

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2007
    Messages
    1 895
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Septembre 2007
    Messages : 1 895
    Points : 4 551
    Points
    4 551
    Par défaut
    Macro vérifiée, certes, mais mal utilisée.

    Bien : j'ai découvert pas moins de 5 bugs dans le code source cité ci-dessus. Je les ai corrigé et - miracle de la technologie moderne - j'ai maintenant le bon résultat. Je vais faire les tests avec les vecteurs de test NIST (zip) mais à priori, on devrait être OK.

    Bon, les bugs maintenant :

    1) dans sha256(), le second appel à prepare() (après la boucle donc) est foireux. Le deuxième paramètre devrait être (length - chunk) et non pas (length - chunk_size), qui n'a aucun sens.

    2) dans sha256(), les valeurs calculées de hash sont stockées en little endian (logique : on fait tous les calculs en little endian) ; avant de les recopier dans le vecteur résultat, il faut les remettre en big endian. Donc
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    unsigned char* huc = reinterpret_cast<unsigned char*>(hash);
    std::copy(huc, huc + sizeof(hash), output.begin());
    est remplacé par
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    uint32_t* out = reinterpret_cast<uint32_t*>(&output.front());
    for (std::size_t i=0; i<8; ++i)
    {
      out[i] = BSWAP32(hash[i]);
    }
    3) dans do_rounds(), a,...,h sont initialisé dans chaque itération, et le résultat est reporté dans hash[] dans chaque itération. C'est faux. L'initialisation et le report dans hash[] doivent se faire à l'extérieur de la boucle. Le code devient :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
     
    		void do_rounds(const uint32_t*w, uint32_t* hash, std::size_t round_count)
    		{
    			uint32_t a = hash[0];
    			uint32_t b = hash[1];
    			uint32_t c = hash[2];
    			uint32_t d = hash[3];
    			uint32_t e = hash[4];
    			uint32_t f = hash[5];
    			uint32_t g = hash[6];
    			uint32_t h = hash[7];
     
    			for (std::size_t round=0; round < round_count; ++round)
    			{
    				details::cycle(w, round, a, b, c, d, e, f, g, h);
    			}
     
    			hash[0] += a;
    			hash[1] += b;
    			hash[2] += c;
    			hash[3] += d;
    			hash[4] += e;
    			hash[5] += f;
    			hash[6] += g;
    			hash[7] += h;
    		}
    Ca, c'est une erreur idiote, que j'aurais pu voir en relisant mieux la doc du NIST.

    4) dans prepare(), dans le cas length < 64, wc[length] est correctement initialisé à 0x80, sauf qu'il est écrasé juste après par la boucle, qui commence à i = length. Correction : la boucle doit commencer à i = length + 1, afin de ne pas écraser wc[length]. Ca aussi, j'aurais pu le voir aisément.

    5) Dans prepare(), dans le cas length < 64, la partie finale du message est mal initialisée, pour une raison que je ne comprends pas. Les 64 derniers bits sont censés être la longueur en bit du message (avant padding), soit (full_length * 8). Cet entier est censé être en big endian. Donc fort logiquement, le code w64[7] = BSWAP64(full_length * 8) devrait être correct. Pour une raison que j'ignore, ce n'est pas le cas (non pas que la macro soit foireuse : je l'ai testé et retesté hier soir ; elle fonctionne). J'ai essayé de faire une copie byte à byte, de cette valeur, même résultat.

    Et le code qui marche est le suivant :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
      uint64_t blen = full_length * 8;
      w[14] = static_cast<uint32_t>(blen >> 32);
      w[15] = static_cast<uint32_t>(blen);
    Les mots hi et low sont swapés mais sont stockés en little endian. Etrangement, ça fonctionne (pour une longueur de 52 bytes, on a bien 0x00000000 0x000001a0, ce qui est attendu).

    Soit. Ca ne me plait pas plus que ça d'intégrer quelque chose que je ne comprends pas vraiment, mais admettons. Si quelqu'un à une idée pour expliquer ce phénomène, qu'il n'hésite pas à m'en parler ; j'en serais fort aise

    Au final, le code de prepare() est:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
     
    		void prepare(const unsigned char* in, std::size_t length, std::size_t full_length, uint32_t* w)
    		{
    			if (length < 64)
    			{
    				memcpy(w, in, length);
     
    				unsigned char* wc = reinterpret_cast<unsigned char*>(w);
    				wc[length] = 0x80;
    				for (std::size_t i=length+1; i<56; ++i)
    				{
    					wc[i] = 0;
    				}
    				uint64_t blen = full_length * 8;
    				w[14] = static_cast<uint32_t>(blen >> 32);
    				w[15] = static_cast<uint32_t>(blen);
    				for (std::size_t i=0; i<14; ++i)
    				{
    					w[i] = BSWAP32(w[i]);
    				}
    			}
    			else
    			{
    				const uint32_t* in32 = reinterpret_cast<const uint32_t*>(in);
    				for (std::size_t i=0; i<16; ++i)
    				{
    					w[i] = BSWAP32(in32[i]);
    				}
    			}
     
    			for (std::size_t r = 16; r < 64; ++r)
    			{
    				w[r] = s1(w[r-2]) + w[r-7] + s0(w[r-15]) + w[r-16];
    			}
    		}
    Le code complet, avec toutes les corrections (le header ne change pas) :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
     
    #include <iostream>
    #include <cstring>
    #include <tr1/cstdint>
    #include "security_sha_digest.h"
    #include "stdio.h"
    #include "stdlib.h"
     
    namespace ekogen { namespace security {
     
    	namespace 
    	{
     
    		typedef std::tr1::uint32_t uint32_t;
    		typedef std::tr1::uint64_t uint64_t;
     
    		inline unsigned int rotleft(uint32_t value, int shift) 
    		{
    			if ((shift &= 31) == 0) return value;
    			return (value << shift) | (value >> (32 - shift));
    		}
     
    		inline unsigned int rotright(uint32_t value, int shift) 
    		{
    			if ((shift &= 31) == 0) return value;
    			return (value >> shift) | (value << (32 - shift));
    		}
     
    	}
     
    #define BSWAP32(x) 		(((uint32_t)(x) << 24) | \
    						 ((uint32_t)(x) >> 24) | \
    						 (((uint32_t)(x) << 8) & 0xff0000L) | \
    						 (((uint32_t)(x) >> 8) & 0xff00L))
     
     
    	// SHA-256 constants
    	namespace constants {
     
    		const uint32_t h0 = (0x6a09e667);
    		const uint32_t h1 = (0xbb67ae85);
    		const uint32_t h2 = (0x3c6ef372);
    		const uint32_t h3 = (0xa54ff53a);
    		const uint32_t h4 = (0x510e527f);
    		const uint32_t h5 = (0x9b05688c);
    		const uint32_t h6 = (0x1f83d9ab);
    		const uint32_t h7 = (0x5be0cd19);
     
    		const uint32_t k[64] = 
    		{
    			(0x428a2f98), (0x71374491), (0xb5c0fbcf), (0xe9b5dba5), 
    			(0x3956c25b), (0x59f111f1), (0x923f82a4), (0xab1c5ed5),
    			(0xd807aa98), (0x12835b01), (0x243185be), (0x550c7dc3), 
    			(0x72be5d74), (0x80deb1fe), (0x9bdc06a7), (0xc19bf174),
    			(0xe49b69c1), (0xefbe4786), (0x0fc19dc6), (0x240ca1cc), 
    			(0x2de92c6f), (0x4a7484aa), (0x5cb0a9dc), (0x76f988da),
    			(0x983e5152), (0xa831c66d), (0xb00327c8), (0xbf597fc7), 
    			(0xc6e00bf3), (0xd5a79147), (0x06ca6351), (0x14292967),
    			(0x27b70a85), (0x2e1b2138), (0x4d2c6dfc), (0x53380d13), 
    			(0x650a7354), (0x766a0abb), (0x81c2c92e), (0x92722c85),
    			(0xa2bfe8a1), (0xa81a664b), (0xc24b8b70), (0xc76c51a3), 
    			(0xd192e819), (0xd6990624), (0xf40e3585), (0x106aa070),
    			(0x19a4c116), (0x1e376c08), (0x2748774c), (0x34b0bcb5), 
    			(0x391c0cb3), (0x4ed8aa4a), (0x5b9cca4f), (0x682e6ff3),
    			(0x748f82ee), (0x78a5636f), (0x84c87814), (0x8cc70208), 
    			(0x90befffa), (0xa4506ceb), (0xbef9a3f7), (0xc67178f2)
    		};
    	}
     
    	namespace details {
     
    		inline uint32_t ch(uint32_t x, uint32_t y, uint32_t z)
    		{
    			return (x & y) ^ (~x & z);
    		}
     
    		inline uint32_t maj(uint32_t x, uint32_t y, uint32_t z)
    		{
    			return (x & y) ^ (x & z) ^ (y & z);
    		}
     
    		inline uint32_t sigma0(uint32_t x)
    		{
    			return rotright(x, 2) ^ rotright(x, 13) ^ rotright(x, 22);
    		}
     
    		inline uint32_t sigma1(uint32_t x)
    		{
    			return rotright(x, 6) ^ rotright(x, 11) ^ rotright(x, 25);
    		}
     
    		inline uint32_t s0(uint32_t x)
    		{
    			return rotright(x, 7) ^ rotright(x, 18) ^ (x >> 3);
    		}
     
    		inline uint32_t s1(uint32_t x)
    		{
    			return rotright(x, 17) ^ rotright(x, 19) ^ (x >> 10);
    		}
     
    		inline void cycle(const uint32_t* w, std::size_t round, 
    						  uint32_t& a, uint32_t& b, uint32_t& c, uint32_t& d, 
    						  uint32_t& e, uint32_t& f, uint32_t& g, uint32_t& h)
    		{
    			uint32_t t1 = h + sigma1(e) + ch(e,f,g) + constants::k[round] + w[round];
    			uint32_t t2 = sigma0(a) + maj(a,b,c);
    			h = g;
    			g = f;
    			f = e;
    			e = d + t1;
    			d = c;
    			c = b;
    			b = a;
    			a = t1 + t2;
    		}
     
    		void do_rounds(const uint32_t*w, uint32_t* hash, std::size_t round_count)
    		{
    			uint32_t a = hash[0];
    			uint32_t b = hash[1];
    			uint32_t c = hash[2];
    			uint32_t d = hash[3];
    			uint32_t e = hash[4];
    			uint32_t f = hash[5];
    			uint32_t g = hash[6];
    			uint32_t h = hash[7];
     
    			for (std::size_t round=0; round < round_count; ++round)
    			{
    				details::cycle(w, round, a, b, c, d, e, f, g, h);
    			}
     
    			hash[0] += a;
    			hash[1] += b;
    			hash[2] += c;
    			hash[3] += d;
    			hash[4] += e;
    			hash[5] += f;
    			hash[6] += g;
    			hash[7] += h;
    		}
     
    		void prepare(const unsigned char* in, std::size_t length, std::size_t full_length, uint32_t* w)
    		{
    			if (length < 64)
    			{
    				memcpy(w, in, length);
     
    				unsigned char* wc = reinterpret_cast<unsigned char*>(w);
    				wc[length] = 0x80;
    				for (std::size_t i=length+1; i<56; ++i)
    				{
    					wc[i] = 0;
    				}
    				uint64_t blen = full_length * 8;
    				w[14] = static_cast<uint32_t>(blen >> 32);
    				w[15] = static_cast<uint32_t>(blen);
    				for (std::size_t i=0; i<14; ++i)
    				{
    					w[i] = BSWAP32(w[i]);
    				}
    			}
    			else
    			{
    				const uint32_t* in32 = reinterpret_cast<const uint32_t*>(in);
    				for (std::size_t i=0; i<16; ++i)
    				{
    					w[i] = BSWAP32(in32[i]);
    				}
    			}
     
    			for (std::size_t r = 16; r < 64; ++r)
    			{
    				w[r] = s1(w[r-2]) + w[r-7] + s0(w[r-15]) + w[r-16];
    			}
    		}
    	}
     
    	void sha256(const unsigned char* in, std::size_t length, std::vector<unsigned char>& output)
    	{
    		const std::size_t chunk_size = 512 / 8;
    		const std::size_t round_count = 64;
     
    		// 1. prepare output
    		std::vector<unsigned char>(32).swap(output);
     
    		// 2. initialize
    		uint32_t hash[8] = 
    		{ 
    			constants::h0, 
    			constants::h1, 
    			constants::h2, 
    			constants::h3, 
    			constants::h4, 
    			constants::h5, 
    			constants::h6, 
    			constants::h7 
    		};
     
    		uint32_t w[round_count] = { 0 };
    		std::size_t chunk;
     
    		// 3. first part of the algorithm: all chunks except the last one
    		for (chunk = 0; chunk + chunk_size < length; chunk += chunk_size)
    		{
    			details::prepare(in + chunk, chunk_size, length, w);
    			details::do_rounds(w, hash, round_count);
    		}
     
    		// 4. final round
    		details::prepare(in + chunk, length - chunk, length, w);
    		details::do_rounds(w, hash, round_count);
     
    		uint32_t* out = reinterpret_cast<uint32_t*>(&output.front());
    		for (std::size_t i=0; i<8; ++i)
    		{
    			out[i] = BSWAP32(hash[i]);
    		}
    	}
     
    } }
    Le code a l'air efficace, mais j'ai un peu de mal à écrire un benchmark satisfaisant (et pour lequel je suis sur du nombre d'appel, etc).
    [FAQ des forums][FAQ Développement 2D, 3D et Jeux][Si vous ne savez pas ou vous en êtes...]
    Essayez d'écrire clairement (c'est à dire avec des mots français complets). SMS est votre ennemi.
    Evitez les arguments inutiles - DirectMachin vs. OpenTruc ou G++ vs. Café. C'est dépassé tout ça.
    Et si vous êtes sages, vous aurez peut être vous aussi la chance de passer à la télé. Ou pas.

    Ce site contient un forum d'entraide gratuit. Il ne s'use que si l'on ne s'en sert pas.

  8. #8
    Membre chevronné
    Avatar de Goten
    Profil pro
    Inscrit en
    Juillet 2008
    Messages
    1 580
    Détails du profil
    Informations personnelles :
    Âge : 33
    Localisation : France

    Informations forums :
    Inscription : Juillet 2008
    Messages : 1 580
    Points : 2 205
    Points
    2 205
    Par défaut
    J'avais identifié le problème mentionné en 5 (et corrigé) mais pas le reste. Tu m'as grillé au poteau faut croire. Je relis ça se soir au calme pour comprendre le phénoméne du 5.
    "Hardcoded types are to generic code what magic constants are to regular code." --A. Alexandrescu

  9. #9
    Expert confirmé

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2007
    Messages
    1 895
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Septembre 2007
    Messages : 1 895
    Points : 4 551
    Points
    4 551
    Par défaut
    PS : le code complet est dispo dans l'archive ekogen.security, accessible dans la partie Source C++.
    [FAQ des forums][FAQ Développement 2D, 3D et Jeux][Si vous ne savez pas ou vous en êtes...]
    Essayez d'écrire clairement (c'est à dire avec des mots français complets). SMS est votre ennemi.
    Evitez les arguments inutiles - DirectMachin vs. OpenTruc ou G++ vs. Café. C'est dépassé tout ça.
    Et si vous êtes sages, vous aurez peut être vous aussi la chance de passer à la télé. Ou pas.

    Ce site contient un forum d'entraide gratuit. Il ne s'use que si l'on ne s'en sert pas.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Problème pour implémenter une JProgressBar
    Par lalalala75 dans le forum AWT/Swing
    Réponses: 1
    Dernier message: 21/05/2012, 11h25
  2. Problème pour implémenter l'interface Comparable
    Par scheme dans le forum Langage
    Réponses: 15
    Dernier message: 29/01/2011, 19h31
  3. Problème pour implémenter une Interface
    Par Freud44 dans le forum C#
    Réponses: 2
    Dernier message: 20/11/2009, 13h56
  4. Réponses: 5
    Dernier message: 26/06/2008, 18h27
  5. [MVC] Problème pour l'implémentation
    Par Baptiste Wicht dans le forum MVC
    Réponses: 20
    Dernier message: 24/04/2007, 21h29

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo