In C++ in SHA256 konvertieren

Jay Shaw 12 Oktober 2023
  1. der SHA256-Hashing-Algorithmus
  2. C++-Programm zum Konvertieren von reinem Text in SHA256
  3. Abschluss
In C++ in SHA256 konvertieren

Die SHA256-Konvertierung ist eine kryptografische Hashing-Technik, die seit den 90er Jahren verwendet wird, aber nach dem Aufkommen von Bitcoin und Blockchain an Bedeutung gewann.

Es verwendet eine irreversible One-Way-Hashing-Technik, was bedeutet, dass die endgültige Antwort niemals auf ihre ursprüngliche Nachricht zurückgesetzt werden kann, wodurch sie sehr sicher ist.

Die endgültige Nachricht wird in einem 256-Bit-Format dargestellt und trägt daher die 256 in ihrem Namen.

der SHA256-Hashing-Algorithmus

Der für die SHA256-Konvertierung verwendete Hash-Algorithmus besteht aus vier Teilen: Auffüllen, anfängliche Puffer, Erstellen eines Nachrichtenplans und Komprimierung. Lassen Sie uns verstehen, wie jeder von ihnen in C++ implementiert wird.

Füllbits in SHA256

Der erste Schritt des Hash-Algorithmus für die SHA256-Konvertierung ist das Auffüllen der ursprünglichen Nachricht. Das Auffüllen muss so erfolgen, dass die Nachricht 448 Bit hat, was 64 Bit weniger als 512 Bit sind.

Wenn eine Nachricht mit den Buchstaben abc aufgefüllt werden muss, werden die ersten 24 Bits mit binären Werten von abc aufgefüllt, gefolgt von 424 Bits Nullen, also insgesamt 448 Bits. Die Länge wird in den letzten 64 Bit gespeichert.

Polsterlänge in SHA256

Die ursprüngliche Nachricht wird aufgefüllt, um sie 512 Bit lang zu machen, bevor sie für das Hashing verwendet wird. Die Nachricht muss so aufgefüllt werden, dass die ersten 448 Bits die Nachricht speichern, während die verbleibenden 64 Datenbits Längenauffüllung sind.

Diese ungenutzten Bits werden verwendet, um die Länge der Nachricht zu verfolgen. Diese 64 Bits sind meistens mit Nullen aufgefüllt, mit Ausnahme der ersten Ziffer, die 1 sein muss, und der verbleibenden letzten Ziffern, die die Nachrichtenlänge speichern.

Puffer für Hashing in SHA256

Der SHA256-Konvertierungsalgorithmus muss Puffer oder Hash-Werte für die Komprimierung initialisieren. Diese Puffer werden Zustandsregister genannt.

Beim SHA256-Hashing beginnt der Prozess immer mit demselben Satz von Zustandsregistern, und diese werden dann wiederholt mit neuen Werten aus dem Nachrichten-Digest angehängt.

Es gibt insgesamt 8 staatliche Register. Nehmen Sie zum besseren Verständnis diese Zustandsregister als Schlitze wahr, die vordefinierte Zufallswerte tragen.

Die Zufallswerte werden gefunden, indem der Modul der Quadratwurzel der ersten 8 Primzahlen genommen und dann mit 2 hoch 32 multipliziert wird.

Der SHA256-Algorithmus erstellt mit dieser Methode die 8 Zustandsregister.

In C++ werden diese Zustandsregister initialisiert, indem hexadezimale Werte dieser 8 Primzahlen direkt einem Array zugewiesen werden.

Die Initialisierung wird im Folgenden demonstriert:

void SHA256::state_register() {
  s_r[0] = 0x6a09e667;
  s_r[1] = 0xbb67ae85;
  s_r[2] = 0x3c6ef372;
  s_r[3] = 0xa54ff53a;
  s_r[4] = 0x510e527f;
  s_r[5] = 0x9b05688c;
  s_r[6] = 0x1f83d9ab;
  s_r[7] = 0x5be0cd19;
  m_len = 0;
  m_tot_len = 0;
}

Außerdem wird ein weiterer Schlüsselsatz benötigt, der 64 Hexadezimalwerte in einem Array speichert, das von hash_keys[0] bis hash_keys[63] reicht.

const unsigned int SHA256::hash_keys[64] = {
    0xef6685ff, 0x38fd3da,  0x94402b15, 0xc67cb7b7, 0x780e38cd, 0xe7440103,
    0x5d415e6e, 0xbb7c2922, 0xf1df8153, 0x5f47e03f, 0x8c658cf7, 0x95ca718,
    0x678d5436, 0xda792dc4, 0x4aa3778b, 0x449e3719, 0x23913e93, 0xfdb2380c,
    0x2e82c771, 0x5bb60bcd, 0x13f53664, 0x174004ae, 0xc338e749, 0x199adec,
    0x28a3dcfe, 0x36fc4894, 0xe1a019cc, 0x59b7fe92, 0x5b007153, 0x1bb32e0d,
    0x2cba796a, 0x3a159148, 0x266d057b, 0xbc9c1d52, 0x17601e7,  0x39b3ccc7,
    0x10367db5, 0xa3558c1b, 0xbf98037f, 0x6fbffc84, 0xef54e44,  0x961a993a,
    0x33e5297b, 0xd2dce255, 0x7fe9864c, 0xfdd93543, 0xc62f137,  0x14eea06b,
    0x2f106df2, 0xf7956237, 0xd053bbca, 0x7a449ecf, 0x8af91f64, 0x9f34a155,
    0x663002e3, 0x7acf8b9c, 0xb0c90a35, 0xa71bba61, 0xc2d6c5a3, 0x9af20609,
    0x8cfc5464, 0x29d95bcf, 0x7c5478b,  0xde9f4ec3};

Nachrichtenplan in SHA256

Ein Meldeplan umfasst 16 Wörter und wird aus jedem Meldeblock gebildet. Dieser Zeitplan ist jeweils 32 Bit lang und besteht aus 16 unterschiedlichen Blöcken binärer Zahlen.

Der Meldeblock muss jedoch 64 Worte lang sein. Dies erfolgt durch Kompilieren von Wörtern, die bereits im Nachrichtenplan vorhanden sind, um neue Blöcke zu erstellen.

Ein Nachrichtenplan muss vorbereitet werden, bevor die Nachricht komprimiert wird. Der 512-Bit-Nachrichtenblock wird in 16 32-Bit-Wörter zerlegt.

Die Konvention besteht darin, ein neues Wort mit den vorhandenen Wörtern zu erstellen, indem bitweise Operationen wie XOR- und AND-Operationen verwendet werden. Der 17. Block wird mit der Formel erstellt:

$$ W(16) = ROTL(\sigma1(W(t-2)) + W(t-7) + \sigma0(W(t-15)) + W(t-16) $$

Wo,

$$ \sigma0(x) = ROTR(7)x + ROTR(18)x + SHR(3)x $$
$$ \sigma1(x) = ROTR(17)x + ROTR(19)x + SHR(10)x $$

Initialisierung der Rotationsfunktionen sigma 0 und sigma 1:

#define SHAF_3(x) (R_ROTATE(x, 7) ^ R_ROTATE(x, 18) ^ R_SHFT(x, 3))
#define SHAF_4(x) (R_ROTATE(x, 17) ^ R_ROTATE(x, 19) ^ R_SHFT(x, 10))

Entstehung des Wortes W(16):

int m;
int n;
for (m = 0; m < (int)block_nb; m++) {
  sub_block = message + (m << 6);
  for (n = 0; n < 16; n++) {
    SHAF_PACK32(&sub_block[n << 2], &w[n]);
  }
  for (n = 16; n < 64; n++) {
    w[n] = SHAF_4(w[n - 2]) + w[n - 7] + SHAF_3(w[n - 15]) + w[n - 16];
  }
  for (n = 0; n < 8; n++) {
    buffer[n] = s_r[n];
  }

Diese Rotationsfunktionen verwenden die vorhandenen Daten innerhalb des Blocks, um sie zu verschmelzen und den Nachrichtenplan mit vielen neuen Bits zu erweitern.

Komprimierung in SHA256

Dies ist der Kern der Hashing-Funktion. Jedes Bit wird zusammengeschaltet und übereinander gelegt, um den endgültigen Nachrichtenauszug zu erstellen.

Diese Funktion verwendet die oben erstellten Statusregister und kombiniert sie mit dem Nachrichtenplan und dem ursprünglichen Nachrichtenblock, um einen Digest zu generieren.

Jede SHA256-Konvertierungs-Hashing-Funktion beginnt mit denselben Zustandsregistern, bevor sie modifiziert wird. Die Funktion erstellt zwei temporäre Wörter mit:

$$ T1 = \sigma1(e) + Ch(e,f,g) + h + k(0) + W(0) $$
$$ T2 = \sigma0(a) + Maj(a,b,c) $$

Dabei steht Ch(e,f,g) für Funktionswahl und Maj(a,b,c) für Funktionsmajor. Diese beiden Funktionen werden unten im Detail erklärt.

Sobald diese temporären Wörter erstellt sind, fügt die Funktion die beiden temporären Wörter hinzu, verschiebt jedes Wort im Statusregister um eine Stelle nach unten und füllt das hinzugefügte Wort im ersten Statusregister, und T1 wird zum Register e hinzugefügt.

Im folgenden Code ist zu sehen, dass buffer[] Zustandsregister sind. Der Wert im 8. Register (buffer[7]) wird mit dem Wert im 7. Register (buffer[6]) vertauscht.

Der Swapping-Vorgang wird fortgesetzt, bis alle Register mit buffer[0] einen neuen Wert erhalten oder das 1. Register die Summe aus T1 und T2 erhält.

Dieser Komprimierungsprozess wird für alle 64 Wörter fortgesetzt und hinterlässt schließlich ein aktualisiertes Zustandsregister.

for (n = 0; n < 64; n++) {
  t1 = buffer[7] + SHAF_2(buffer[4]) +
       CHOICE_OF(buffer[4], buffer[5], buffer[6]) + hash_keys[n] + w[n];
  t2 = SHAF_1(buffer[0]) + MAJORITY_OF(buffer[0], buffer[1], buffer[2]);
  buffer[7] = buffer[6];
  buffer[6] = buffer[5];
  buffer[5] = buffer[4];
  buffer[4] = buffer[3] + t1;
  buffer[3] = buffer[2];
  buffer[2] = buffer[1];
  buffer[1] = buffer[0];
  buffer[0] = t1 + t2;
}
for (n = 0; n < 8; n++) {
  s_r[n] += buffer[n];

Hier sind CHOICE_OF() und MAJORITY_OF Wahl- und Hauptfunktionen. Diese sind definiert als:

#define CHOICE_OF(x, y, z) ((x & y) ^ (~x & z))
#define MAJORITY_OF(x, y, z) ((x & y) ^ (x & z) ^ (y & z))

Wo:

  • x & y = x und y
  • ~x = Negation von x
  • ^ = XOR

Der letzte Teil der Komprimierung für diesen Nachrichtenblock besteht darin, die anfänglichen Hash-Werte, mit denen wir begonnen haben, zu nehmen und sie mit dem Ergebnis der Komprimierung zu addieren. Die endgültigen Hash-Werte werden in Hexadezimalzahlen konvertiert und verkettet, um den endgültigen Nachrichtenauszug zu erstellen.

C++-Programm zum Konvertieren von reinem Text in SHA256

Dieses Programm besteht aus zwei Teilen – einer SHA256-Header-Datei, die alle wesentlichen definierten Funktionen enthält, und einer C++-Hauptprogrammdatei.

Header-Datei für den SHA256-Algorithmus in C++

#ifndef HASHFUNCTIONS_H
#define HASHFUNCTIONS_H
#include <string>

class hash_functions {
 protected:
  typedef unsigned char register_8;
  typedef unsigned int register_32;
  typedef unsigned long long register_64;

  const static register_32 hash_keys[];
  static const unsigned int BLOCK_SIZE_of_256 = (512 / 8);

 public:
  void stateregister();  // init
  void adjust_digest(const unsigned char *text, unsigned int text_len);
  void digest_final(unsigned char *digest);
  static const unsigned int PADD_SIZE = (256 / 8);

 protected:
  void compress(const unsigned char *message, unsigned int block_nb);
  unsigned int s_r_totlen;
  unsigned int s_r_len;
  unsigned char s_r_block[2 * BLOCK_SIZE_of_256];
  register_32 s_r[8];
};

std::string sha256(std::string input);

#define R_SHFT(x, n) (x >> n)  // Right shift function
#define R_ROTATE(x, n) \
  ((x >> n) | (x << ((sizeof(x) << 3) - n)))  // Right rotate function
#define L_ROTATE(x, n) \
  ((x << n) | (x >> ((sizeof(x) << 3) - n)))     // Left rotate function
#define CHOICE_OF(x, y, z) ((x & y) ^ (~x & z))  // function to find choice of
#define MAJORITY_OF(x, y, z) \
  ((x & y) ^ (x & z) ^ (y & z))  // function to find majority of
#define SHAF_1(x)                     \
  (R_ROTATE(x, 2) ^ R_ROTATE(x, 13) ^ \
   R_ROTATE(x, 22))  // sigma rotation function
#define SHAF_2(x)                     \
  (R_ROTATE(x, 6) ^ R_ROTATE(x, 11) ^ \
   R_ROTATE(x, 25))  // sigma rotation function
#define SHAF_3(x) \
  (R_ROTATE(x, 7) ^ R_ROTATE(x, 18) ^ R_SHFT(x, 3))  // sigma0 rotation
#define SHAF_4(x) \
  (R_ROTATE(x, 17) ^ R_ROTATE(x, 19) ^ R_SHFT(x, 10))  // sigma1 rotation
#define SHAF_UNPACK32(x, str)               \
  {                                         \
    *((str) + 3) = (register_8)((x));       \
    *((str) + 2) = (register_8)((x) >> 8);  \
    *((str) + 1) = (register_8)((x) >> 16); \
    *((str) + 0) = (register_8)((x) >> 24); \
  }
#define SHAF_PACK32(str, x)                      \
  {                                              \
    *(x) = ((register_32) * ((str) + 3)) |       \
           ((register_32) * ((str) + 2) << 8) |  \
           ((register_32) * ((str) + 1) << 16) | \
           ((register_32) * ((str) + 0) << 24);  \
  }
#endif

Hauptdatei zum Ausführen von SHA256 in C++

#include <cstring>
#include <fstream>
#include <iostream>

#include "hash_functions.h"

const unsigned int hash_functions::hash_keys[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};

void hash_functions::compress(const unsigned char *message,
                              unsigned int block_nb) {
  register_32 w[64];
  register_32 buffer[8];
  register_32 t1, t2;
  const unsigned char *sub_block;
  int m;
  int n;
  for (m = 0; m < (int)block_nb; m++) {
    sub_block = message + (m << 6);
    for (n = 0; n < 16; n++) {
      SHAF_PACK32(&sub_block[n << 2], &w[n]);
    }
    for (n = 16; n < 64; n++) {
      w[n] = SHAF_4(w[n - 2]) + w[n - 7] + SHAF_3(w[n - 15]) + w[n - 16];
    }
    for (n = 0; n < 8; n++) {
      buffer[n] = s_r[n];
    }
    for (n = 0; n < 64; n++) {
      t1 = buffer[7] + SHAF_2(buffer[4]) +
           CHOICE_OF(buffer[4], buffer[5], buffer[6]) + hash_keys[n] + w[n];
      t2 = SHAF_1(buffer[0]) + MAJORITY_OF(buffer[0], buffer[1], buffer[2]);
      buffer[7] = buffer[6];
      buffer[6] = buffer[5];
      buffer[5] = buffer[4];
      buffer[4] = buffer[3] + t1;
      buffer[3] = buffer[2];
      buffer[2] = buffer[1];
      buffer[1] = buffer[0];
      buffer[0] = t1 + t2;
    }
    for (n = 0; n < 8; n++) {
      s_r[n] += buffer[n];
    }
  }
}

void hash_functions::stateregister() {
  s_r[0] = 0x6a09e667;
  s_r[1] = 0xbb67ae85;
  s_r[2] = 0x3c6ef372;
  s_r[3] = 0xa54ff53a;
  s_r[4] = 0x510e527f;
  s_r[5] = 0x9b05688c;
  s_r[6] = 0x1f83d9ab;
  s_r[7] = 0x5be0cd19;
  s_r_len = 0;
  s_r_totlen = 0;
}

void hash_functions::adjust_digest(const unsigned char *text,
                                   unsigned int text_len) {
  unsigned int block_nb;
  unsigned int new_len, rem_len, tmp_len;
  const unsigned char *shifted_message;
  tmp_len = BLOCK_SIZE_of_256 - s_r_len;
  rem_len = text_len < tmp_len ? text_len : tmp_len;
  memcpy(&s_r_block[s_r_len], text, rem_len);
  if (s_r_len + text_len < BLOCK_SIZE_of_256) {
    s_r_len += text_len;
    return;
  }
  new_len = text_len - rem_len;
  block_nb = new_len / BLOCK_SIZE_of_256;
  shifted_message = text + rem_len;
  compress(s_r_block, 1);
  compress(shifted_message, block_nb);
  rem_len = new_len % BLOCK_SIZE_of_256;
  memcpy(s_r_block, &shifted_message[block_nb << 6], rem_len);
  s_r_len = rem_len;
  s_r_totlen += (block_nb + 1) << 6;
}

void hash_functions::digest_final(unsigned char *digest) {
  unsigned int block_nb;
  unsigned int pm_len;
  unsigned int len_b;
  int i;
  block_nb = (1 + ((BLOCK_SIZE_of_256 - 9) < (s_r_len % BLOCK_SIZE_of_256)));
  len_b = (s_r_totlen + s_r_len) << 3;
  pm_len = block_nb << 6;
  memset(s_r_block + s_r_len, 0, pm_len - s_r_len);
  s_r_block[s_r_len] = 0x80;
  SHAF_UNPACK32(len_b, s_r_block + pm_len - 4);
  compress(s_r_block, block_nb);
  for (i = 0; i < 8; i++) {
    SHAF_UNPACK32(s_r[i], &digest[i << 2]);
  }
}

std::string sha256(std::string input) {
  unsigned char digest[hash_functions::PADD_SIZE];
  memset(digest, 0, hash_functions::PADD_SIZE);

  hash_functions obj = hash_functions();
  obj.stateregister();
  obj.adjust_digest((unsigned char *)input.c_str(), input.length());
  obj.digest_final(digest);

  char buf[2 * hash_functions::PADD_SIZE + 1];
  buf[2 * hash_functions::PADD_SIZE] = 0;
  for (int i = 0; i < hash_functions::PADD_SIZE; i++)
    sprintf(buf + i * 2, "%02x", digest[i]);
  return std::string(buf);
}

using std::cout;
using std::endl;
using std::string;

int main(int argc, char *argv[]) {
  string text_in = "Paris";
  string text_out = sha256(text_in);

  cout << "Final Output('" << text_in << "'):" << text_out << endl;
  return 0;
}

Ausgang:

sha256('Paris'):5dd272b4f316b776a7b8e3d0894b37e1e42be3d5d3b204b8a5836cc50597a6b1

--------------------------------
Process exited after 0.03964 seconds with return value 0
Press any key to continue . . .

Abschluss

Dieser Artikel hat die Kryptographie der SHA256-Konvertierung mit C++ im Detail erklärt. Der Hashing-Algorithmus und die Komprimierung des Nachrichtenplans, die das Herzstück des kryptografischen Hashings bilden, werden mit Codeschnipseln demonstriert, um das Verständnis zu erleichtern.

Ein gebrauchsfertiges C++-Programm ist ebenfalls beigefügt, um direkt zum Lernen und Backtesting eingesetzt zu werden.

Verwandter Artikel - C++ Algorithm