2010-02-08 43 views
22

Je suis tombé sur BCrypt.net après avoir lu Jeff Atwood's post about storing passwords qui m'a conduit à la recommandation de Thomas Ptacek à use BCrypt pour stocker les mots de passe. Qui m'a finalement conduit à this C# implementation of BCryptPourquoi BCrypt.net GenerateSalt (31) revient-il tout de suite?

Dans les commentaires sur le dernier lien ci-dessus quelqu'un a demandé "Pourquoi GenerateSalt (30) prend pour toujours, mais GenerateSalt (31) ne semble pas prendre du temps?" J'ai exécuté BCrypt.HashPassword (mot de passe, BCrypt.GenerateSalt (31)) et obtenu mon résultat en 0 millisecondes.

Je cours BCrypt.HashPassword ("mot de passe", BCrypt.GenerateSalt (30)) depuis plus de 5 minutes maintenant et n'a toujours pas de résultat.

Je réalise que nous n'aurons probablement pas besoin d'un sel de 30 caractères généré aléatoirement pour créer nos hachages de mots de passe (ou irreversible encryption in BCrypt's case) depuis des années. EDIT J'aurais dû lire un peu le code, logRounds n'a rien à voir avec la longueur du sel. Merci Aaronaught.

Alors, pourquoi GenerateSalt (31) retourne une valeur presque instantanément (quand il devrait prendre environ deux fois plus longtemps que GenerateSalt (30)

MISE À JOUR

est la solution ici:

private byte[] CryptRaw(byte[] password, byte[] salt, int logRounds) { 
    // ... snip ... 
    uint rounds = 1U << logRounds; 
    // ... snip 
} 

Répondre

25

Je soupçonne que le bug est ici:

private byte[] CryptRaw(byte[] password, byte[] salt, int logRounds) { 
    // ... snip ... 
    int rounds = 1 << logRounds; 
    // ... snip 
} 

Lorsque vous spécifiez 31 pour le logRounds, il calcule que 2^32, ce qui ne rentre pas dans un int et trop-pleins, donc le hachage est réellement fait dans ... euh, zéro passe l'auteur aurait dû utiliser uint à la place Facile à fixer


a également voulu commenter à ce sujet:..!

Je sais que nous allons probablement pas besoin d'un généré au hasard 30 caractères sel pour créer nos hashs ...

Notez que le paramètre logRounds ne se réfère pas au nombre de caractères/octets dans le sel, qui est toujours 16. Il se réfère à la base logarithmique du nombre de passes que le hachage prendra pour calculer; en d'autres termes, c'est une façon de rendre l'échelle de bcrypt avec la loi de Moore, rendant la fonction de plusieurs ordres de grandeur plus coûteuse à calculer si les ordinateurs deviennent suffisamment rapides pour craquer les hachages existants.

+0

Voir, s'ils ont testé 'rounds' (à ce moment-là -2^31) en utilisant' rounds! = 0' au lieu de 'rounds> 0', alors cela aurait quand même fonctionné correctement! :-P –

+1

@Chris: Leçon apprise, écrivez toujours vos boucles 'for' avec'! = 'En cas de débordement! : P C'est exactement pourquoi il est si dangereux pour les gens de rouler leurs propres fonctions de chiffrement; Même les experts se trompent! Je suppose que ce bug n'a jamais été signalé car personne n'a jamais essayé 31 rondes de journaux auparavant ... – Aaronaught

+0

est-ce la bonne correction: uint rounds = 1U << logRounds; –

10

Si hachant avec GenerateSalt(31) retours presque instantanément, c'est un bug. Vous devez signaler qu'en amont (je, pour jBCrypt). :-)

Par défaut, les rondins sont de 10. Cela signifie que (si je me souviens bien), 1024 coups sont utilisés. Chaque fois que vous incrémentez les rondins, le nombre de tours est doublé.

À 30 rondins, vous effectuez 1073741824 tours. Cela prend beaucoup de temps. À 31 rondes de log, 2147483648 tours devraient être faits, mais je soupçonne que l'implémentation particulière que vous utilisez déborde à la place. :-(

+0

cela semble une explication très probable pour moi ... Je vais +1 même si je ne sais pas à coup sûr :) – rmeador

+1

a du sens: si vous doublez chaque fois et il échoue à 31, il y a probablement un 32bit entier impliqué (moins un bit pour signe) quelque part qui devrait utiliser un long ou décimal à la place. –