cerceis-lib
Version:
Contains list of quality of life functions that is written in TypeScript and es6
215 lines (205 loc) • 8.08 kB
text/typescript
import { primeConst, compressionHash } from "./constants.js"
export const Sha256 = (inputStr: string) => {
const _toBinary = (text: string): string => {
let rs: string = ""
for (let i = 0; i < text.length; i++) {
rs += text[i].charCodeAt(0).toString(2).padStart(8, "0")
}
return rs
}
//Shift right
//Rotate right
//XOR
//ADD <= remember to keep it in 32 bit range
const _rotr = (arr: string[], attempt: number = 1): string[] => {
let tmp: any[] = [...arr]
for (let i = 0; i < attempt; i++) {
tmp = [tmp.pop(), ...tmp]
}
return tmp
}
const _shr = (arr: string[], attempt: number = 1): string[] => {
let tmp: any[] = [...arr]
for (let i = 0; i < attempt; i++) {
tmp.pop()
tmp.unshift("0")
}
return tmp
}
const _xor = (arrA: string[], arrB: string[], arrC: string[]): string[] => {
const tmp: string[] = []
arrA.forEach((bit, index) => {
tmp[index] = String(Number(bit) ^ Number(arrB[index]) ^ Number(arrC[index]))
})
return tmp
}
const _add = (arrA: string[], arrB: string[]): string[] => {
const tmp: string[] = []
const len: number = arrA.length;
let extra: number = 0
for (let i = arrA.length - 1; i >= 0; i--) {
const bitValue: number = Number(arrA[i]) + Number(arrB[i]) + extra
tmp[i] = bitValue > 1 ? String(bitValue - 2) : String(bitValue)
extra = bitValue > 1 ? 1 : 0
}
return tmp
}
/**
* Functions
* choice, given x,y,z. If x = 1 takes y, if x = 0 takes z
* major, given x,y,z. If numbers of 1 > 0 then 1. else 0.
*/
const _choice = (arrA: string[], arrB: string[], arrC: string[]): string[] => {
let tmp: string[] = []
arrA.forEach((bit, index) => {
tmp[index] = bit === "1" ? arrB[index] : arrC[index];
})
return tmp;
}
const _major = (arrA: string[], arrB: string[], arrC: string[]): string[] => {
let tmp: string[] = []
arrA.forEach((bit, index) => {
const val: string = bit + arrB[index] + arrC[index]
if (
val === "000" || val === "001" ||
val === "010" || val === "100"
) tmp[index] = "0"
else if (
val === "111" || val === "110" ||
val === "101" || val === "011"
) tmp[index] = "1"
})
return tmp;
}
/**
* Compound movements
* sigZero = ROTR 7, ROTR 18, SHR 3, XOR
* sigOne = ROTR 17, ROTR 19, SHR 10, XOR
* uSigZero = ROTR 2, ROTR 13, ROTR 22, XOR
* uSigOne = ROTR 6, ROTR 11, ROTR 25, XOR
*/
const _sigZero = (arr: string[]): string[] => {
const rotr7 = _rotr(arr, 7)
const rotr18 = _rotr(arr, 18)
const shr3: string[] = _shr(arr, 3)
return _xor(rotr7, rotr18, shr3)
}
const _sigOne = (arr: string[]): string[] => {
const rotr17 = _rotr(arr, 17)
const rotr19 = _rotr(arr, 19)
const shr10: string[] = _shr(arr, 10)
return _xor(rotr17, rotr19, shr10)
}
const _uSigZero = (arr: string[]): string[] => {
const rotr2 = _rotr(arr, 2)
const rotr13 = _rotr(arr, 13)
const rotr22 = _rotr(arr, 22)
return _xor(rotr2, rotr13, rotr22)
}
const _uSigOne = (arr: string[]): string[] => {
const rotr6 = _rotr(arr, 6)
const rotr11 = _rotr(arr, 11)
const rotr25 = _rotr(arr, 25)
return _xor(rotr6, rotr11, rotr25)
}
const shiftCompressionMap = (compressionMap: {[key: string]: string[]}): void => {
compressionMap["h"] = [...compressionMap["g"]];
compressionMap["g"] = [...compressionMap["f"]];
compressionMap["f"] = [...compressionMap["e"]];
compressionMap["e"] = [...compressionMap["d"]];
compressionMap["d"] = [...compressionMap["c"]];
compressionMap["c"] = [...compressionMap["b"]];
compressionMap["b"] = [...compressionMap["a"]];
compressionMap["a"] = []
}
/**
* Structure will be:
* The binary of the message + 1 as separator + padding of 0 until 448 bits + length of the message with start padding of 0.
* End result will be total of 512 in length
*/
let messageBin: string = _toBinary(inputStr)
const messageBlocks: string[] = [];
const messageLengthBin = messageBin.length.toString(2).padStart(64, "0")
const blockCount: number = Math.ceil((messageBin.length + 1 + 64) / 512)
const finalLength = blockCount * 512
/*
messageBin += "1" //Separator
messageBin = messageBin.padEnd(512 * blockCount - 64, "0") //padding of 0 until 448 bits
messageBin += messageLengthBin //Remaining 64 with length of the message with start padding of 0.
*/
// 𝑘=512𝑖+447−𝑚
/**
* k = paddings
* i = blockCount
* m = input length
* Pre-processing (Padding):
begin with the original message of length L bits
append a single '1' bit
append K '0' bits, where K is the minimum number >= 0 such that L + 1 + K + 64 is a multiple of 512
append L as a 64-bit big-endian integer, making the total post-processed length a multiple of 512 bits
such that the bits in the message are L 1 00..<K 0's>..00 <L as 64 bit integer> = k*512 total bits
*/
messageBin += "1" //Separator
messageBin = messageBin.padEnd(finalLength-64, "0") //padding of 0 until 448 bits
messageBin += messageLengthBin //Remaining 64 with length of the message with start padding of 0.
for (let i = 0; i < blockCount; i++) {
let messageChunks = messageBin.slice(i * 512, i * 512 + 512)
//Add padding to last block
messageBlocks.push(messageChunks)
}
let finalCompression = {...compressionHash}
//message schedule
messageBlocks.forEach((block, bi) => {
let localCompression = {...finalCompression}
const words: string[][] = [];
for (let i = 0; i < 16; i++) {
words.push(block.slice(i * 32, i * 32 + 32).split(""))
}
//Words need to be 64 in length, so we gonna expand it
//sigOne(-2) + (-7) + sigZero(-15) + (-16)
while(words.length < 64){
words.push(
_add(
_add(
_add(_sigOne(words[words.length-2]), words[words.length-7]),
_sigZero(words[words.length-15])
),
words[words.length-16]
)
)
}
//Compression
/**
* T1 = uSigOne(e) + choice(e,f,g) + h + K0 + W0
* T2 = uSigZero(a) + major(a,b,c)
*/
words.forEach((word, index) => {
const t1: string[] = _add(
_add(
_add(
_add(_uSigOne(localCompression.e), _choice(localCompression.e, localCompression.f, localCompression.g)),
localCompression.h
),
primeConst[index]
),
word
)
const t2: string[] = _add(
_uSigZero(localCompression.a), _major(localCompression.a, localCompression.b, localCompression.c)
)
shiftCompressionMap(localCompression)
localCompression.a = _add(t1, t2)
localCompression.e = _add(localCompression.e, t1)
})
for(let id in localCompression){
if(bi === 0)
finalCompression[id] = _add(compressionHash[id], localCompression[id])
else
finalCompression[id] = _add(finalCompression[id], localCompression[id])
}
})
let result: string = ""
for(let id in finalCompression)
result += parseInt(finalCompression[id].join(""), 2).toString(16)
return result
}