Created
April 28, 2023 09:45
-
-
Save theoparis/23ff33fe720d21a5bfa0d67365883c19 to your computer and use it in GitHub Desktop.
thash zig + bun backup
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const modes = { | |
argon2d: 0, | |
argon2i: 1, | |
argon2id: 2, | |
}; | |
export type Mode = keyof typeof modes; | |
export type WasmExports = { | |
memory: { buffer: ArrayBuffer }; | |
hptr: () => number; | |
cad: () => void; | |
free: (ptr: number, size: number) => void; | |
sad: (ptr: number, size: number) => void; | |
alloc: (size: number) => number; | |
ssecret: (ptr: number, size: number) => void; | |
csecret: () => void; | |
hash_password: ( | |
ptr: number, | |
size: number, | |
mode: number, | |
time: number, | |
memory: number, | |
) => boolean; | |
verify_password: ( | |
ptr: number, | |
size: number, | |
hashPtr: number, | |
hashSize: number, | |
) => boolean; | |
}; | |
export type HashOptions = { | |
t?: number; | |
m?: number; | |
mode?: Mode; | |
}; | |
const utf8_encode = TextEncoder.prototype.encode.bind(new TextEncoder()); | |
const utf8_decode = TextDecoder.prototype.decode.bind(new TextDecoder()); | |
import { readFile } from "fs/promises"; | |
export const load = async () => { | |
const file = await readFile(new URL("thash.wasm", import.meta.url), "buffer"); | |
const module = new WebAssembly.Module(file as unknown as BufferSource); | |
const instance = new WebAssembly.Instance(module, { | |
wasi_snapshot_preview1: { | |
fd_write() { | |
throw null; | |
}, | |
random_get(ptr: number, size: number) { | |
if (!wasm) throw new Error("WebAssembly module not loaded"); | |
return crypto.getRandomValues( | |
new Uint8Array(wasm.memory.buffer, ptr, size), | |
); | |
}, | |
}, | |
}); | |
const wasm = instance.exports as unknown as WasmExports; | |
const hptr = wasm.hptr(); | |
const presets = Object.assign(Object.create(null), { | |
argon2i: Object.assign(Object.create(null), { | |
moderate: { t: 6, m: 134217728 / 1024 }, | |
sensitive: { t: 8, m: 536870912 / 1024 }, | |
interactive: { t: 4, m: 33554432 / 1024 }, | |
}), | |
argon2id: Object.assign(Object.create(null), { | |
moderate: { t: 3, m: 67108864 / 1024 }, | |
sensitive: { t: 4, m: 1073741824 / 1024 }, | |
interactive: { t: 2, m: 67108864 / 1024 }, | |
}), | |
}); | |
// TODO: extract core functions into @theoparis/twasm | |
const u8 = (buf: BufferSource | number) => { | |
if (buf instanceof Uint8Array) return buf; | |
if (ArrayBuffer.isView(buf)) | |
return new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength); | |
if (buf instanceof ArrayBuffer || buf instanceof SharedArrayBuffer) | |
return new Uint8Array(buf); | |
throw new TypeError("expected a buffer"); | |
}; | |
const ad = (ad: number) => { | |
if (!wasm) throw new Error("WebAssembly module not loaded"); | |
if (null == ad) return wasm.cad(); | |
const buf = "string" !== typeof ad ? u8(ad) : utf8_encode(ad); | |
const ptr = wasm.alloc(buf.length); | |
new Uint8Array(wasm.memory.buffer, ptr, buf.length).set(buf); | |
wasm.sad(ptr, buf.length); | |
}; | |
const secret = (secret: string) => { | |
if (!wasm) throw new Error("WebAssembly module not loaded"); | |
if (null == secret) return wasm.csecret(); | |
const buf = "string" !== typeof secret ? u8(secret) : utf8_encode(secret); | |
const ptr = wasm.alloc(buf.length); | |
new Uint8Array(wasm.memory.buffer, ptr, buf.length).set(buf); | |
wasm.ssecret(ptr, buf.length); | |
}; | |
const hashPassword = (pswd: string, options?: HashOptions) => { | |
if (!wasm) throw new Error("WebAssembly module not loaded"); | |
const buf = "string" !== typeof pswd ? u8(pswd) : utf8_encode(pswd); | |
const ptr = wasm.alloc(buf.length); | |
new Uint8Array(wasm.memory.buffer, ptr, buf.length).set(buf); | |
try { | |
if ( | |
!wasm.hash_password( | |
ptr, | |
buf.length, | |
modes[options?.mode ?? "argon2id"] ?? modes.argon2id, | |
options?.t ?? presets.argon2i.moderate.t, | |
options?.m ?? presets.argon2i.moderate.m, | |
) | |
) | |
return null; | |
return utf8_decode(new Uint8Array(wasm.memory.buffer, hptr, 128)); | |
} finally { | |
wasm.free(ptr, buf.length); | |
} | |
}; | |
const verifyPassword = (pswd: string, hash: string) => { | |
if (!wasm) throw new Error("WebAssembly module not loaded"); | |
const passwordBuffer = | |
"string" !== typeof pswd ? u8(pswd) : utf8_encode(pswd); | |
const passwordPointer = wasm.alloc(passwordBuffer.length); | |
new Uint8Array( | |
wasm.memory.buffer, | |
passwordPointer, | |
passwordBuffer.length, | |
).set(passwordBuffer); | |
const hashBuffer = "string" !== typeof hash ? u8(hash) : utf8_encode(hash); | |
const hashPointer = wasm.alloc(hashBuffer.length); | |
new Uint8Array(wasm.memory.buffer, hashPointer, hashBuffer.length).set( | |
hashBuffer, | |
); | |
try { | |
if ( | |
!wasm.verify_password( | |
passwordPointer, | |
passwordBuffer.length, | |
hashPointer, | |
hashBuffer.length, | |
) | |
) | |
return false; | |
return true; | |
} finally { | |
wasm.free(passwordPointer, passwordBuffer.length); | |
} | |
}; | |
return { | |
hashPassword, | |
verifyPassword, | |
}; | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const std = @import("std"); | |
const builtin = @import("builtin"); | |
var ad: ?[]const u8 = null; | |
var out: [128]u8 = undefined; | |
const allocator = std.heap.page_allocator; | |
var secret: ?[]const u8 = null; | |
export fn hptr() *[128]u8 { | |
return &out; | |
} | |
export fn cad() void { | |
if (ad) |s| allocator.free(s); | |
ad = null; | |
} | |
export fn free(ptr: [*]u8, size: usize) void { | |
allocator.free(ptr[0..size]); | |
} | |
export fn csecret() void { | |
if (secret) |s| allocator.free(s); | |
secret = null; | |
} | |
export fn sad(ptr: [*]u8, size: usize) void { | |
if (ad) |s| allocator.free(s); | |
ad = ptr[0..size]; | |
} | |
export fn alloc(size: usize) ?[*]u8 { | |
return (allocator.alloc(u8, size) catch return null).ptr; | |
} | |
export fn ssecret(ptr: [*]u8, size: usize) void { | |
if (secret) |s| allocator.free(s); | |
secret = ptr[0..size]; | |
} | |
export fn hash_password(ptr: [*]const u8, size: usize, mode: u8, time: u32, memory: u32) bool { | |
_ = std.crypto.pwhash.argon2.strHash(ptr[0..size], .{ | |
.allocator = allocator, | |
.mode = @intToEnum(std.crypto.pwhash.argon2.Mode, mode), | |
.params = .{ | |
.p = 1, | |
.ad = ad, | |
.t = time, | |
.m = memory, | |
.secret = secret, | |
}, | |
}, &out) catch return false; | |
return true; | |
} | |
export fn verify_password( | |
password_ptr: [*]const u8, | |
password_size: usize, | |
hash_ptr: [*]const u8, | |
hash_size: usize, | |
) bool { | |
_ = std.crypto.pwhash.argon2.strVerify( | |
hash_ptr[0..hash_size], | |
password_ptr[0..password_size], | |
.{ | |
.allocator = allocator, | |
}, | |
) catch return false; | |
return true; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment