Skip to content

Instantly share code, notes, and snippets.

@Yengas
Created June 1, 2015 14:33
Show Gist options
  • Select an option

  • Save Yengas/eb0764b53d028480a8dd to your computer and use it in GitHub Desktop.

Select an option

Save Yengas/eb0764b53d028480a8dd to your computer and use it in GitHub Desktop.
SHA-1 Hash Function Implementation
#include "SHA.h"
#include <cmath>
#include <iostream>
#include <iomanip>
uint32_t stageOneFunction(uint32_t b, uint32_t c, uint32_t d) {
return (b & c) | ((~b) & d);
}
uint32_t stageTwoFunction(uint32_t b, uint32_t c, uint32_t d) {
return b ^ c ^ d;
}
uint32_t stageThreeFunction(uint32_t b, uint32_t c, uint32_t d) {
return (b & c) | (b & d) | (c & d);
}
uint32_t circularShift(uint32_t word, int amount) {
return (word << amount) | (word >> (32 - amount));
}
uint32_t getWord(uint32_t* word, int round) {
if(round < 16)
return word[round];
return circularShift((getWord(word, round - 16) ^ getWord(word, round - 14) ^ getWord(word, round - 8) ^ getWord(word, round - 3)), 1);
}
const uint32_t SHA::constants[4] = { 0x5A827999, 0x6ED9EBA1, 0x8F1BBCDC, 0xCA62C1D6 }, SHA::registerStart[5] = { 0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0 };
const roundFunction SHA::functions[4] = { stageOneFunction, stageTwoFunction, stageThreeFunction, stageTwoFunction };
void SHA::reset() {
registers[0] = 0x67452301;
registers[1] = 0xEFCDAB89;
registers[2] = 0x98BADCFE;
registers[3] = 0x10325476;
registers[4] = 0xC3D2E1F0;
read = 0;
block_index = 0;
padded = false;
}
void SHA::update(uint8_t* data, uint64_t length) {
for(uint64_t i = 0; i < length; i++) {
block[block_index++] = data[i];
read += 8;
if(block_index == 64) {
transform();
}
}
}
void SHA::round(uint32_t word, uint32_t constant, roundFunction function) {
uint32_t temp = registers[4] + function(registers[1], registers[2], registers[3]) + circularShift(registers[0], 5) + word + constant;
registers[4] = registers[3];
registers[3] = registers[2];
registers[2] = circularShift(registers[1], 30);
registers[1] = registers[0];
registers[0] = temp;
}
void SHA::padding() {
block[block_index++] = 0x80;
if(block_index > 56) {
transform();
memset(block, 0, 56);
}else {
memset(block + block_index, 0, 56 - block_index);
}
for(int i = 0; i < 8; i++) {
block[56 + i] = (read >> ((8 - i - 1) * 8)) & 0xFF;
}
transform();
}
void SHA::transform() {
if(padded) this->reset();
uint32_t beforeRegisters[5];
memcpy(beforeRegisters, registers.data(), sizeof(uint32_t) * 5);
uint32_t W[80];
for(int x = 0; x < 80; x++) {
if(x < 16) {
W[x] = 0;
for(int i = 0; i < 4; i++) {
W[x] = (W[x] << 8) | block[x * 4 + i];
}
}else {
W[x] = circularShift(W[x - 16] ^ W[x - 14] ^ W[x - 8] ^ W[x - 3], 1);
}
}
for(int i = 0; i < 80; i++) {
int stage = i / 20;
round(W[i], constants[stage], functions[stage]);
}
for(int i = 0; i < registers.size(); i++) {
registers[i] = (registers[i] + beforeRegisters[i]) & 0xFFFFFFFF;
}
block_index = 0;
}
std::string SHA::digest() {
if(!padded) {
padding();
padded = true;
}
std::stringstream result;
result.fill('0');
for(int i = 0; i < 5; i++) {
result << std::hex << std::uppercase << std::setw(8) << registers[i];
}
return result.str();
}
#pragma once
#include <stdint.h>
#include <string>
#include <sstream>
#include <array>
typedef uint32_t(*roundFunction)(uint32_t b, uint32_t c, uint32_t d);
class SHA
{
public:
SHA(){ this->reset(); };
void reset();
void update(uint8_t* data, uint64_t size);
std::string digest();
private:
void round(uint32_t word, uint32_t constant, roundFunction function);
void transform();
void padding();
bool padded = false;
uint64_t read = 0;
uint8_t block_index = 0;
uint8_t block[64];
std::array<uint32_t, 5> registers;
static const uint32_t constants[], registerStart[];
static const roundFunction functions[];
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment