Wie führen Sie eine doppelte SHA-256-Codierung durch?

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.

Nur Semantik, aber um ein häufiges Missverständnis zu vermeiden: sha256 hasht , nicht kodiert. Codieren ist etwas ganz anderes. Zum einen impliziert es, dass es dekodiert werden kann, während Hashing ausschließlich eine einseitige (und destruktive) Operation ist.

Antworten (7)

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
Das scheint richtig zu sein, aber wie hat die erste Runde funktioniert, als die ASCII-Zeichenfolge "HALLO" im Vergleich zur binären Darstellung von ASCII-Hallo gehasht wurde?
@makerofthings7 "HALLO" ist binär. H ist ein Byte mit dem Wert 72, E ist 69 usw. Ein roher, binärer Hash kann auch gedruckt werden, aber viele seiner Zeichen sind wahrscheinlich unsichtbare Steuerzeichen, daher wird er im Allgemeinen in Hex geschrieben. Ich kann auch "HALLO" in Hex schreiben: es ist 48454c4c4f.
@makerofthings7: Es gibt keinen Unterschied zwischen "der ASCII-Zeichenfolge 'HALLO' und "der binären Darstellung von ASCII 'HALLO'". Es gibt jedoch einen Unterschied zwischen der ASCII-Darstellung einer Zahl im Hexadezimalformat und dieser Zahl im Binärformat.
theymos und @DavidSchwartz macht jetzt alles vollkommen Sinn. Danke
Hallo, gibt es eine optimierte (direkte und schnellere) Möglichkeit, SHA256d auszuführen? Einige openssloder ä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'
danke für Codebeispiele. Später fand ich den Python-Code im Wiki, konnte aber immer noch nicht verstehen, warum er funktionierte. +1

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-9und a-fist und es fin 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 cryptoModul 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 ....

Benötigte Header sind:

#include <string>
#include <cstring>
#include <assert.h> // assert() is used

Die verwendete Sha256-Implementierung ist die von Zedwood !!

Die erste ist die ParseHexvon 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 sha256Funktionsaufruf.


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
}

  • DIESE FUNKTIONEN MÜSSEN IN DIE SHA256-HEADER-DATEI EINGEFÜGT WERDEN
  • SIE MÜSSEN ENDIANESS DES HASH-ERGEBNISSES UMKEHREN.
  • AUCH DAS "assert()" LÖST EINEN FEHLER BEI UNGLEICH LÄNGENDEN EINGABEN AUS. ALSO FUNKTIONIERT "abc" NICHT.

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.