hello git
This commit is contained in:
commit
3c308ccb21
4 changed files with 279 additions and 0 deletions
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
package-lock.json
|
||||||
|
node_modules/
|
||||||
|
test.txt
|
209
IGA.ts
Normal file
209
IGA.ts
Normal file
|
@ -0,0 +1,209 @@
|
||||||
|
type padding = {
|
||||||
|
padding: number[]
|
||||||
|
seed: number
|
||||||
|
}
|
||||||
|
|
||||||
|
type verifyOut = {
|
||||||
|
valid: boolean
|
||||||
|
reason: string
|
||||||
|
}
|
||||||
|
|
||||||
|
function modMul(n: number, x: number, m: number): number {
|
||||||
|
let out = 0
|
||||||
|
for (let i = 0; i < x; i++) {
|
||||||
|
out = out + n
|
||||||
|
out = out % m
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
class randomGenerator {
|
||||||
|
state = 0
|
||||||
|
seen: number[] = []
|
||||||
|
constructor(seed: number) {
|
||||||
|
this.state = seed
|
||||||
|
}
|
||||||
|
|
||||||
|
next(): number {
|
||||||
|
let temp = modMul(this.state, 6967, 65536)
|
||||||
|
if (this.seen.length >= 65536) {
|
||||||
|
this.seen = []
|
||||||
|
}
|
||||||
|
while (this.seen.includes(temp)) {
|
||||||
|
temp++
|
||||||
|
temp = temp % 65536
|
||||||
|
}
|
||||||
|
this.seen.push(temp)
|
||||||
|
this.state = temp
|
||||||
|
return this.state
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function generatePadding(len: number, seed?: number): padding {
|
||||||
|
if (seed == undefined) {
|
||||||
|
seed = Math.floor(Math.random() * 65535)
|
||||||
|
}
|
||||||
|
let out: number[] = []
|
||||||
|
let generator = new randomGenerator(seed)
|
||||||
|
for (let i = 0; i < len; i++) {
|
||||||
|
let n = generator.next()
|
||||||
|
out.push(n)
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
padding: out,
|
||||||
|
seed: seed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function copy(input: number[]): number[] {
|
||||||
|
let out: number[] = []
|
||||||
|
for (let i = 0; i < input.length; i++) {
|
||||||
|
out[i] = input[i]
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
function dec2bin(dec: number): string {
|
||||||
|
return (dec >>> 0).toString(2);
|
||||||
|
}
|
||||||
|
function bin2dec(bin: string): number {
|
||||||
|
return parseInt(bin, 2)
|
||||||
|
}
|
||||||
|
function fixbin(bin: string, len: number): string {
|
||||||
|
let out = bin
|
||||||
|
while (out.length < len) {
|
||||||
|
out = "0" + out
|
||||||
|
}
|
||||||
|
return out.substring(out.length-len, out.length)
|
||||||
|
}
|
||||||
|
function bin2hex(bin: string): string {
|
||||||
|
let chars = ["0","1","2","3","4","5","6","7","8","9","A","B","C","D","E","F"]
|
||||||
|
let hex = ""
|
||||||
|
while (bin.length > 3) {
|
||||||
|
let n = bin2dec(bin.substring(0, 4))
|
||||||
|
hex += chars[n]
|
||||||
|
bin = bin.substring(4, bin.length)
|
||||||
|
}
|
||||||
|
return hex
|
||||||
|
}
|
||||||
|
function hex2bin(hex: string): string {
|
||||||
|
let chars = ["0","1","2","3","4","5","6","7","8","9","A","B","C","D","E","F"]
|
||||||
|
let bin = ""
|
||||||
|
for (let i = 0; i < hex.length; i++) {
|
||||||
|
bin += fixbin(dec2bin(chars.indexOf(hex[i].toUpperCase())), 4)
|
||||||
|
}
|
||||||
|
return bin
|
||||||
|
}
|
||||||
|
|
||||||
|
function checksum(input: string) {
|
||||||
|
let state: string[] = []
|
||||||
|
for (let i = 0; i < 16; i++) {
|
||||||
|
state.push("0")
|
||||||
|
}
|
||||||
|
let bin = ""
|
||||||
|
for (let i = 0; i < input.length; i++) {
|
||||||
|
bin += dec2bin(input.charCodeAt(i))
|
||||||
|
}
|
||||||
|
while (bin.length > 0) {
|
||||||
|
state.splice(0, 1)
|
||||||
|
state.push(bin[0])
|
||||||
|
if (state[0] == "1") {
|
||||||
|
state[8] = state[8] == "1" ? "0" : "1"
|
||||||
|
state[6] = state[6] == "1" ? "0" : "1"
|
||||||
|
state[1] = state[1] == "1" ? "0" : "1"
|
||||||
|
}
|
||||||
|
bin = bin.substring(1, bin.length)
|
||||||
|
}
|
||||||
|
return state.join("")
|
||||||
|
}
|
||||||
|
|
||||||
|
function hash(input: string, salt: number = 0, iterations: number = 32, len: number = 32, seed?: number): string {
|
||||||
|
if (len*4%16 != 0) {
|
||||||
|
throw new Error("Invalid length")
|
||||||
|
}
|
||||||
|
if (len < 16) {
|
||||||
|
throw new Error("Length must be at least 16")
|
||||||
|
}
|
||||||
|
const padding = generatePadding(len, seed);
|
||||||
|
let hash: number[] = []
|
||||||
|
for (let i = 0; i < padding.padding.length; i++) {
|
||||||
|
hash[i] = padding.padding[i]
|
||||||
|
}
|
||||||
|
let plus = hash.length
|
||||||
|
for (let i = 0; i < input.length; i++) {
|
||||||
|
hash[plus+i] = input.charCodeAt(i)
|
||||||
|
}
|
||||||
|
for (let i = 0; i < iterations; i++) {
|
||||||
|
let state = copy(hash)
|
||||||
|
for (let ii = 0; ii < hash.length; ii++) {
|
||||||
|
let temp = state[ii] + (state[ii+1] ? state[ii+1] : state[0]) + salt
|
||||||
|
while (temp > 65536) {
|
||||||
|
temp -= i==0 ? 2411 : (state[ii-1] ? state[ii-1] : state[state.length-1])
|
||||||
|
}
|
||||||
|
hash[ii] = temp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const pSeed = fixbin(dec2bin(padding.seed), 16)
|
||||||
|
let sumAdd = ""
|
||||||
|
for (let i = 0; sumAdd.length < 16 && i < padding.padding.length; i++) {
|
||||||
|
sumAdd += padding.padding[i].toString(16)
|
||||||
|
}
|
||||||
|
sumAdd = sumAdd.substring(0, 16).toUpperCase()
|
||||||
|
const sum = checksum(input + sumAdd)
|
||||||
|
let bin = ""
|
||||||
|
for (let i = 0; bin.length < len*4-32; i++) {
|
||||||
|
bin += hash[i] ? fixbin(dec2bin(hash[i]), 16) : "0000000000000000"
|
||||||
|
}
|
||||||
|
const outBin = pSeed + bin + sum
|
||||||
|
const out = bin2hex(outBin)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
function verifyHash(input: string, inHash: string, salt: number = 0, iterations: number = 32, len: number = 32): verifyOut {
|
||||||
|
if (len*4%16 != 0) {
|
||||||
|
throw new Error("Invalid length")
|
||||||
|
}
|
||||||
|
if (len < 16) {
|
||||||
|
throw new Error("Length must be at least 16")
|
||||||
|
}
|
||||||
|
if (inHash.length != len) {
|
||||||
|
throw new Error("The hash is not " + len + " long")
|
||||||
|
}
|
||||||
|
const hBin = hex2bin(inHash)
|
||||||
|
const pSeed = bin2dec(hBin.substring(0, 16))
|
||||||
|
const inSum = hBin.substring(hBin.length-16, hBin.length)
|
||||||
|
const padding = generatePadding(len, pSeed);
|
||||||
|
let sumAdd = ""
|
||||||
|
for (let i = 0; sumAdd.length < 16 && i < padding.padding.length; i++) {
|
||||||
|
sumAdd += padding.padding[i].toString(16)
|
||||||
|
}
|
||||||
|
sumAdd = sumAdd.substring(0, 16).toUpperCase()
|
||||||
|
const sum = checksum(input + sumAdd)
|
||||||
|
if (inSum != sum) {
|
||||||
|
return {
|
||||||
|
valid: false,
|
||||||
|
reason: "Checksum does not match"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const verifyHash = hash(input, salt, iterations, len, pSeed)
|
||||||
|
if (inHash == verifyHash) {
|
||||||
|
return {
|
||||||
|
valid: true,
|
||||||
|
reason: ""
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
valid: false,
|
||||||
|
reason: "Hash does not match"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
hash,
|
||||||
|
verifyHash
|
||||||
|
}
|
||||||
|
|
||||||
|
export type {
|
||||||
|
verifyOut
|
||||||
|
}
|
62
hash.ts
Normal file
62
hash.ts
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
import IGA from './IGA';
|
||||||
|
import fs from 'fs';
|
||||||
|
|
||||||
|
const args = process.argv.slice(2)
|
||||||
|
const version = "1.1"
|
||||||
|
|
||||||
|
function printHelp() {
|
||||||
|
console.log("IGA commandline tool v" + version)
|
||||||
|
console.log("Usage: tsx hash.ts <hash|verify> <input file> [<hash to verify>] [options]");
|
||||||
|
console.log("Options:");
|
||||||
|
console.log("[<salt = 0>] [<iterations = 32>] [<length of the hash = 32>]");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args[0] == "hash") {
|
||||||
|
if (args.length < 2) {
|
||||||
|
console.log("Not enough arguments");
|
||||||
|
printHelp()
|
||||||
|
process.exit(0)
|
||||||
|
}
|
||||||
|
if (!fs.existsSync(args[1])) {
|
||||||
|
console.log("File not found: " + args[1])
|
||||||
|
process.exit(0)
|
||||||
|
}
|
||||||
|
let fileData = fs.readFileSync(args[1], 'utf8');
|
||||||
|
let salt = args[2] ? parseInt(args[2]) : 0
|
||||||
|
let iterations = args[3] ? parseInt(args[3]) : 32
|
||||||
|
let len = args[4] ? parseInt(args[4]) : 32
|
||||||
|
try {
|
||||||
|
let hash = IGA.hash(fileData, salt, iterations, len)
|
||||||
|
console.log(hash)
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Error while creating hash: " + e.stack)
|
||||||
|
}
|
||||||
|
} else if (args[0] == "verify") {
|
||||||
|
if (args.length < 3) {
|
||||||
|
console.log("Not enough arguments");
|
||||||
|
printHelp()
|
||||||
|
process.exit(0)
|
||||||
|
}
|
||||||
|
if (!fs.existsSync(args[1])) {
|
||||||
|
console.log("File not found: " + args[1])
|
||||||
|
process.exit(0)
|
||||||
|
}
|
||||||
|
let fileData = fs.readFileSync(args[1], 'utf8');
|
||||||
|
let hashToVerify = args[2]
|
||||||
|
let salt = args[3] ? parseInt(args[3]) : 0
|
||||||
|
let iterations = args[4] ? parseInt(args[4]) : 32
|
||||||
|
let len = args[5] ? parseInt(args[5]) : 32
|
||||||
|
try {
|
||||||
|
let verify = IGA.verifyHash(fileData, hashToVerify, salt, iterations, len)
|
||||||
|
if (verify.valid) {
|
||||||
|
console.log("Hash is valid!")
|
||||||
|
} else {
|
||||||
|
console.log("Hash is invalid!")
|
||||||
|
console.log("Reason: " + verify.reason)
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Error while verifying hash: " + e.stack)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
printHelp()
|
||||||
|
}
|
5
package.json
Normal file
5
package.json
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"dependencies": {
|
||||||
|
"@types/node": "^22.15.17"
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue