Die Bitcoin -Protokollspezifikation gibt ein Beispiel für eine doppelte SHA-256-Codierung.
hallo
2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824 (erste Runde von sha-256)
9595c9df90075148eb06860365df33584b75bff782a510c6cd486 von sha-02d486
Ich habe verschiedene SHA256 - Rechner ausprobiert und die erste Codierung stimmt problemlos überein, aber die zweite löst sich immer auf
d7914fe546b684688bb95f4f888a92dfc680603a75f23eb823658031fff766d9
Ich habe auch UPPERCASE ausprobiert und die Byte-Endianness ausgetauscht.
Sie hashen die hexadezimale Darstellung des ersten Hashs. Sie müssen den eigentlichen Hash hashen – die binären Daten, die das Hex darstellt.
Versuche dies:
$ echo -n hello |openssl dgst -sha256 -binary |openssl dgst -sha256
openssl
oder ähnliche (zuverlässige) optimierte Algorithmen dafür?Sie möchten mit den Digests arbeiten, nicht mit den Hex-Strings.
Hier ist etwas Rubin:
require 'digest'
d = Digest::SHA2.new 256
d2 = Digest::SHA2.new 256
d << 'hello'
d.to_s
d2 << d.digest
d2.to_s
Dies wird die Ausgabe von sein irb
:
1.9.3p194 :001 > require 'digest'
=> true
1.9.3p194 :003 > d = Digest::SHA2.new 256
=> #<Digest::SHA2:256 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855>
1.9.3p194 :004 > d2 = Digest::SHA2.new 256
=> #<Digest::SHA2:256 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855>
1.9.3p194 :005 > d << 'hello'
=> #<Digest::SHA2:256 2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824>
1.9.3p194 :006 > d.to_s
=> "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"
1.9.3p194 :007 > d2 << d.digest
=> #<Digest::SHA2:256 9595c9df90075148eb06860365df33584b75bff782a510c6cd4883a419833d50>
1.9.3p194 :008 > d2.to_s
=> "9595c9df90075148eb06860365df33584b75bff782a510c6cd4883a419833d50"
Hier ist das gleiche in Python:
import hashlib
d = hashlib.sha256(b"hello")
d2 = hashlib.sha256()
d.hexdigest()
d2.update(d.digest())
d2.hexdigest()
Und die Ausgabe aus einer Python-Shell:
>>> d = hashlib.sha256(b"hello")
>>> d2 = hashlib.sha256()
>>> d.hexdigest()
'2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824'
>>> d2.update(d.digest())
>>> d2.hexdigest()
'9595c9df90075148eb06860365df33584b75bff782a510c6cd4883a419833d50'
Für die zweite Runde von sha256 müssen Sie die rohe Binärausgabe der ersten Runde hashen, nicht die Textversion.
Ein sha256-Hash hat 256 Bit oder 32 Byte. Daher sollten Sie für die zweite Runde ein Datenstück mit 32 Bytes hashen. Beim Hashen einer hexadezimalen Zeichenfolge als wörtliche Eingabe für die zweite Runde sind Ihre Daten 64 Bytes groß.
Probieren Sie ein Hashing-Tool aus, das hexadezimale Eingaben interpretieren kann. Siehe zum Beispiel hier , kopieren Sie den Hash in das Eingabefeld und aktivieren Sie das Kontrollkästchen „Hex“.
Benennen Sie der Einfachheit halber die verschiedenen Eingaben und Ausgaben aus Ihrer Frage wie folgt:
pre-image 1: hello
|
v
hash operation 1: SHA256
|
v
hash output 1 (aka pre-image 2): 2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824
|
v
hash operation 2: SHA256
|
v
hash output 2: 9595c9df90075148eb06860365df33584b75bff782a510c6cd4883a419833d50
Pre-Image 1 sind binäre (ASCII) Daten. Wir wissen das, weil der Bereich der hexadezimalen Zeichen 0-9
und a-f
ist und es f
in Pre-Image 1 noch Zeichen gibt. Daher muss die Hash-Operation 1 mit einer binären Eingabe ausgeführt werden.
Per Definition erzeugt eine SHA256-Hash-Operation immer eine 256-Bit-Ganzzahl. Wir stellen dies normalerweise im Hexadezimalformat dar, es könnte jedoch genauso korrekt im Dezimalformat geschrieben werden, und nichts würde sich ändern:
hex: 2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824
decimal: 20329878786436204988385760252021328656300425018755239228739303522659023427620
(Dezimalumwandlung mit Wolfram Alpha)
Da wir wissen, dass Pre-Image 2 eine Hexadezimalzahl ist, muss die Hash-Operation 2 an einer Zahl und nicht an binären Daten durchgeführt werden.
In Javascript würden die beiden Hashes unter Verwendung der Stanford Javascript Crypto Library wie folgt erstellt:
var preImage1 = 'hello';
var hashOutput1 = sjcl.hash.sha256.hash(preImage1);
console.log('hash output 1: ' + sjcl.codec.hex.fromBits(hashOutput1));
var preImage2 = hashOutput1;
var hashOutput2 = sjcl.hash.sha256.hash(preImage2);
console.log('hash output 2: ' + sjcl.codec.hex.fromBits(hashOutput2));
Die SJCL ist schlau genug, um die Pre-Image-Formate herauszufinden und den Hash in jedem Fall korrekt auszuführen.
Sie können diese Hash-Operationen auch live in meinem Blog durchführen, hier: https://analysis.null.place/how-do-the-bitcoin-mining-algorithms-work/#form10
Beachten Sie, wie sich die Hash-Ausgabe ändert, wenn Sie auf das Kontrollkästchen Pre-image is hexadecimal klicken (dies funktioniert nur, wenn hexadezimale Zeichen in der Eingabe vorhanden sind).
Spät zur Party, aber hier ist eine Node.js-Implementierung, die das eingebaute crypto
Modul verwendet:
const crypto = require('crypto');
/**
*
* @param {Buffer} data
* @returns {Buffer}
*/
function doubleSHA256(data) {
return crypto.createHash('sha256').update(crypto.createHash('sha256').update(data).digest()).digest();
}
Hoffe das hilft.....
Musste leichte Änderungen an der ParseHex()
von Andrew Chow bereitgestellten vornehmen ....
#include <string>
#include <cstring>
#include <assert.h> // assert() is used
Die verwendete Sha256-Implementierung ist die von Zedwood !!
Die erste ist die ParseHex
von Andrew Chow bereitgestellte Funktion
std::string ParseHex(std::string& s)
{
assert(s.size() % 2 == 0);
static const std::size_t symbol_count = 256;
static const unsigned char hex_to_bin[symbol_count] =
{
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x00 - 0x07
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x08 - 0x0F
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x10 - 0x17
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x18 - 0x1F
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x20 - 0x27
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x28 - 0x2F
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // 0x30 - 0x37
0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x38 - 0x3F
0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00, // 0x40 - 0x47
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x48 - 0x4F
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x50 - 0x57
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x58 - 0x5F
0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00, // 0x60 - 0x67
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x68 - 0x6F
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x70 - 0x77
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x78 - 0x7F
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x80 - 0x87
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x88 - 0x8F
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x90 - 0x97
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x98 - 0x9F
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xA0 - 0xA7
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xA8 - 0xAF
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xB0 - 0xB7
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xB8 - 0xBF
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xC0 - 0xC7
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xC8 - 0xCF
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xD0 - 0xD7
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xD8 - 0xDF
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xE0 - 0xE7
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xE8 - 0xEF
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xF0 - 0xF7
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // 0xF8 - 0xFF
};
std::string out;
auto itr = s.begin();
while (itr != s.end())
{
unsigned char b = static_cast<unsigned char>(hex_to_bin[*(itr++)] << 4);
b |= static_cast<unsigned char>(hex_to_bin[*(itr++)] );
out.push_back(b);
}
return out;
}
Als nächstes folgt der modifizierte sha256
Funktionsaufruf.
std::string sha256(std::string input)
{
string kk;
int size = input.length();
unsigned char header_data[size], hash[32];// header_data is "160" in size because midstate optimization is not implemented
char buf[2*32+1];
buf[2*32] = 0;
input = ParseHex(input); // ParseHex() is UNDEFINED !!!
init(); // Initiialize
update((unsigned char*)input.c_str(), input.length()); // Update
final(hash); // Finalize
// Uncomment code below if you want to directly double hash input data using a single "sha256(input)" function call
// init();
// update(hash, 32);
// final(hash);
for (int i = 0; i < 32; i++)
sprintf(buf+i*2, "%02x", hash[i]); // feed data from hash into buf and format data
return std::string(buf); // Return buf as string instead of char
}
Hoffe, das hilft dir, wenn du das jemals brauchst ....
Prost!!
Es sieht so aus, als hätten Sie Ihren Algorithmus so codiert, dass er immer davon ausgeht, dass Ihre Eingabenachricht immer Daten im ASCII-Anzeigeformat enthält. Das würde erklären, warum Ihr erster Algo die richtige Hash-Tabelle erzeugt hat. Für run2 können Sie Ihre Eingabe-MSG-Daten in jedem beliebigen Format haben, solange Sie Ihrem Algorithmus auf irgendeine Weise mitteilen können, was er zu erwarten hat. Ich gehe davon aus, dass Sie beim Schreiben Ihres Algos immer das ASCII-Anzeigeformat in der Eingabe-msg-Datei erwartet haben. Wenn Sie also Ihre erste Ausgabeblocktabelle nicht auf das ASCII-Anzeigeformat umgestellt haben, würde dies erklären, warum Sie bei Ihrem zweiten Lauf nicht die erwartete endgültige Blocktabelle erhalten haben. Schnappen Sie sich eine Liste von "The IBM PC Character Set" und Sie werden sehen, was ich meine. zB wenn Sie ein 'a' angeben und es wirklich ein hexadezimales 'a' ist, im Gegensatz zu einem ASCII-Anzeigeformat ' a', dann MÜSSEN Sie Ihren Algo wissen lassen, was Sie tun - er kann nicht zweifeln, dass Sie das Format gewechselt haben, ohne es ihm zu sagen. In ähnlicher Weise haben Sie möglicherweise den hexadezimalen Hash oder sogar die binäre Hash-Tabelle von der Ausgabe von run1 in die Eingabe von run2 eingefügt, was in Ordnung ist, ABER Sie haben Ihrem Algo mitgeteilt, dass er es als das von Ihnen gewählte Format lesen soll, um es zu verwenden die run2-Eingabedatei. Es gibt 2 Möglichkeiten, dies IMO zu beheben. (1) Konvertieren Sie Ihre Ausgabe-Hash-Blocktabelle von run1 in ASCII-Anzeigezeichen und packen Sie sie in die Eingabedatei von run2. Ich würde das nicht tun, weil Sie einige seltsame und wunderbare ASCII-Anzeigezeichen erhalten, die seltsame Dinge tun können. (2) Codieren Sie Ihren Algo neu, damit Sie ihm sagen können, welche Daten aus der run2-Eingabedatei kommen werden. Das ist eine flexiblere Art, Dinge zu tun, und es eröffnet viel mehr Optionen in Bezug auf die Verwendung jeder beliebigen Basis, sogar Ihrer eigenen benutzerdefinierten, außer YMMV. Seit der Veröffentlichung Ihres Problems ist viel Wasser unter die Brücke geflossen, aber das kann anderen helfen.
RucolaNüsse