Hallo Chessfreak3,
der Divisor 456976 ist 26 hoch 4, also eine Potenz weniger als die Anzahl der möglichen Passworte.
HashFromSerial generiert aus einer Codezahl ein fünfstelliges Passwort. Du kannst Dir das so vorstellen, als würde eine Zahl aus dem Dezimalsystem ins 26-er System konvertiert, wobei a den Ziffernwert 0 und z den Ziffernwert 25 hat.
Ich mach's mal andersrum: Die Buchstaben im Passwort "selfi" haben die Ziffernwerte 20, 4, 11, 5 und 8.
Die 20 hat den Stellenwert $$26^4$$ = 456976 Die 4 hat den Stellenwert $$26^3$$ = 17576 Die 11 hat den Stellenwert $$26^2$$ = 676 Die 5 hat den Stellenwert $$26^1$$ = 26 Die 8 hat den Stellenwert $$26^0$$ = 1
Die Codenummer zum Passwort ist also $$20 \cdot 456976 + 4 \cdot 17576 + 11 \cdot 676 + 5 \cdot 26 + 8 \cdot 1 \\
= 9139520 + 70304 + 7436 + 130 + 8 \\
= 9217398$$
Keine Garantie für Tippfehlerfreiheit.
Wenn Du Dir nicht vorstellen kannst, was das soll, dann stell Dir vor, du hast Worte aus den Buchstaben a bis j. Das a hat dann den Ziffernwert 0 und das j den Ziffernwert 9. Nun möchtest Du das Wort "jabba" verschlüsseln. Die Basis ist hier 10, das Wort hat 5 Stellen, darum bekommt das j den Stellenwert $$10^4$$ = 10000. Das zweite Zeichen, 'a', hat den Ziffernwert 0 und den Stellenwert $$10^3$$ = 1000.
"jabba" -> $$9 \cdot 10000 + 0\cdot 1000 + 1\cdot 100 + 1\cdot 10 + 0\cdot 1 = 90110$$. Hier ist die 1:1 Zuordnung von Ziffer zu Stelle direkt zu sehen, und du siehst auch sofort, dass jeder Buchstabe schön an seiner Stelle bleibt. Wäre auch noch das k zulässig, mit Ziffernwert 10, würde es in die nächste Stelle hineinreichen und den Wert dort stören. Man müsste dann also zur Basis 11 rechnen. Und bei a-z eben mit Basis 26.
HashFromSerial führt diese Rechnung umgekehrt durch und berechnet aus der Codenummer die 5 Ziffernwerte. Wenn Du 9217398 hineingibst, entsteht im ersten Durchlauf der Schleife letter=20 und serial=77878. Auf die 20 wird 97 addiert, weil das der Zeichencode von 'a' ist. Es entsteht der Codewert für ein 's', der wird in letters abgelegt. Danach wird divisor durch 26 dividiert, um den Teiler zum Abtrennen der nächsten Stelle zu bekommen, und es beginnt von vorn. Insgesamt fünf Mal, d.h. danach stehen in letters die 5 Codewerte für 's', 'e', 'l', 'f' und 'i'.
Dieses Array wird in eine Bytekette konvertiert (zumindest lese ich, als Phytonphobiker, bytes(letters) so) und ein sha256-Digest dazu generiert. Der wird dann zusammen mit dem Passwort zurückgegeben.
Dieser Teil passiert sequenziell, ohne jede Parallelisierung.
Die geschieht in p.imap_unordered, wobei ich keine Ahnung habe wie Python hier vorgeht. range(numpasswords) ist zunächstmal eine Sequenz aus knapp 12 Millionen Zahlen, und imap_unordered muss die zunächst in Blocks der vorgegebenen Größe aufteilen. Angeblich gelingt das effizienter als bei p.map(), aber ich kann mir vorstellen, dass das nicht die effizienteste Lösung ist. Aber egal, ich bin kein Schlangenbändiger.
Die for i in range(5)
Schleife in HashFromSerial dividiert den Wert in Serial zunächst durch 456976, d.h. bestimmt die Codenummer des ersten Buchstabens im zu ratenden Passwort. Was vom Dividieren bleibt, der Rest, wird von divmod als zweiter Wert zurückgegeben und wird wieder nach Serial geschrieben.
Die Codenummer wird in einen Kleinbuchstaben umgewandelt und in letters abgelegt. Danach wird divisor durch 26 dividiert, um die nächstkleinere Potenz zu erhalten, und der Zyklus beginnt von vorn.
Rolf
sumpsi - posui - obstruxi