Implement kCTF strategy
This implementation is pretty scuffed, but its more exploratory than anything else.
This commit is contained in:
@@ -2,17 +2,13 @@ const std = @import("std");
|
||||
|
||||
pub fn build(b: *std.Build) void {
|
||||
const optimize = b.standardOptimizeOption(.{});
|
||||
const wasm_target = b.resolveTargetQuery(.{
|
||||
.cpu_arch = .wasm32,
|
||||
.os_tag = .freestanding,
|
||||
.cpu_features_add = std.Target.wasm.featureSet(&.{ .bulk_memory, .bulk_memory_opt, .simd128, .tail_call }),
|
||||
});
|
||||
const target = b.standardTargetOptions(.{});
|
||||
|
||||
// solver
|
||||
const solver_mod = b.addModule("solver", .{
|
||||
.root_source_file = b.path("src/solver.zig"),
|
||||
.optimize = optimize,
|
||||
.target = wasm_target,
|
||||
.target = target,
|
||||
});
|
||||
|
||||
const solver_exe = b.addExecutable(.{
|
||||
@@ -20,10 +16,12 @@ pub fn build(b: *std.Build) void {
|
||||
.root_module = solver_mod,
|
||||
});
|
||||
|
||||
solver_exe.entry = .disabled;
|
||||
solver_exe.rdynamic = true;
|
||||
solver_exe.lto = .full;
|
||||
solver_exe.link_gc_sections = true;
|
||||
if (target.result.cpu.arch == .wasm32) {
|
||||
solver_exe.entry = .disabled;
|
||||
solver_exe.rdynamic = true;
|
||||
solver_exe.link_gc_sections = true;
|
||||
solver_exe.lto = .full;
|
||||
}
|
||||
|
||||
b.installArtifact(solver_exe);
|
||||
|
||||
@@ -31,16 +29,18 @@ pub fn build(b: *std.Build) void {
|
||||
const validator_mod = b.addModule("validator", .{
|
||||
.root_source_file = b.path("src/validator.zig"),
|
||||
.optimize = optimize,
|
||||
.target = wasm_target,
|
||||
.target = target,
|
||||
});
|
||||
|
||||
const validator_exe = b.addLibrary(.{
|
||||
const validator_exe = b.addExecutable(.{
|
||||
.name = "validator",
|
||||
.root_module = validator_mod,
|
||||
});
|
||||
|
||||
validator_exe.entry = .disabled;
|
||||
validator_exe.rdynamic = true;
|
||||
if (target.result.cpu.arch == .wasm32) {
|
||||
validator_exe.entry = .disabled;
|
||||
validator_exe.rdynamic = true;
|
||||
}
|
||||
|
||||
b.installArtifact(validator_exe);
|
||||
}
|
||||
|
||||
@@ -5,3 +5,211 @@ const std = @import("std");
|
||||
const Allocator = std.mem.Allocator;
|
||||
const assert = std.debug.assert;
|
||||
const math = std.math;
|
||||
const Int = math.big.int.Managed;
|
||||
|
||||
var managed_one: ?Int = null;
|
||||
|
||||
fn get_bit(n: *Int, idx: usize) !bool {
|
||||
if (n.len() < idx / @typeInfo(usize).int.bits) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var foo = try n.clone();
|
||||
defer foo.deinit();
|
||||
|
||||
try foo.shiftRight(n, idx);
|
||||
try foo.bitAnd(&foo, &managed_one.?);
|
||||
return foo.eql(managed_one.?);
|
||||
}
|
||||
|
||||
pub fn square_mod(n: *Int) !void {
|
||||
const allocator = n.allocator;
|
||||
try n.sqr(n);
|
||||
|
||||
var high = try Int.init(allocator);
|
||||
defer high.deinit();
|
||||
try high.shiftRight(n, 1279); // high = n >> 1279
|
||||
|
||||
var mask = try Int.init(allocator);
|
||||
defer mask.deinit();
|
||||
|
||||
if (managed_one == null) {
|
||||
managed_one = try Int.init(allocator);
|
||||
try managed_one.?.set(1);
|
||||
}
|
||||
|
||||
try mask.set(1);
|
||||
try mask.shiftLeft(&mask, 1279);
|
||||
try mask.sub(&mask, &managed_one.?);
|
||||
|
||||
try n.bitAnd(n, &mask);
|
||||
|
||||
try n.add(n, &high);
|
||||
|
||||
if (try get_bit(n, 1279)) {
|
||||
// clear bit 1279
|
||||
var power_of_2 = try Int.init(allocator);
|
||||
defer power_of_2.deinit();
|
||||
try power_of_2.set(1);
|
||||
try power_of_2.shiftLeft(&power_of_2, 1279);
|
||||
try n.sub(n, &power_of_2);
|
||||
|
||||
// *n += 1;
|
||||
try n.add(n, &managed_one.?);
|
||||
}
|
||||
}
|
||||
|
||||
pub const Version = "s";
|
||||
|
||||
pub const Challenge = struct {
|
||||
difficulty: ?u32,
|
||||
salt: std.math.big.int.Managed,
|
||||
|
||||
const Self = @This();
|
||||
|
||||
pub fn destroy(self: *Self, allocator: Allocator) void {
|
||||
self.salt.deinit();
|
||||
allocator.destroy(self);
|
||||
}
|
||||
|
||||
pub fn encode(self: *Self, allocator: Allocator) ![]u8 {
|
||||
const solution_base64_len = std.base64.standard.Encoder.calcSize(self.salt.len() * @sizeOf(usize));
|
||||
const dest = try allocator.alloc(u8, solution_base64_len);
|
||||
defer allocator.free(dest);
|
||||
@memset(dest, 0);
|
||||
|
||||
const limbs_u8_buffer: []u8 = std.mem.sliceAsBytes(self.salt.limbs[0..self.salt.len()]);
|
||||
const base64_str = std.base64.standard.Encoder.encode(dest, limbs_u8_buffer);
|
||||
|
||||
return try std.fmt.allocPrint(allocator, "{s}", .{base64_str});
|
||||
}
|
||||
};
|
||||
|
||||
pub fn decode(allocator: Allocator, challenge: []const u8) !*Challenge {
|
||||
var parts = std.mem.splitAny(u8, challenge, ".");
|
||||
if (parts.next()) |part| {
|
||||
if (!std.mem.eql(u8, part, Version)) {
|
||||
return error.InvalidChallenge;
|
||||
}
|
||||
} else {
|
||||
return error.InvalidChallenge;
|
||||
}
|
||||
|
||||
var difficulty: ?u32 = null;
|
||||
var next_part = parts.next() orelse return error.InvalidChallenge;
|
||||
if (parts.peek()) |_| {
|
||||
// must be <version>.<difficulty>.<salt>
|
||||
const difficulty_bytes = try allocator.alloc(u8, try std.base64.standard.Decoder.calcSizeForSlice(next_part));
|
||||
defer allocator.free(difficulty_bytes);
|
||||
|
||||
try std.base64.standard.Decoder.decode(difficulty_bytes, next_part);
|
||||
|
||||
std.log.info("Decoded difficulty bytes: {any}\n", .{difficulty_bytes});
|
||||
|
||||
var difficulty_array: [4]u8 = .{0} ** 4;
|
||||
if (difficulty_bytes.len > 4) {
|
||||
const split_idx = difficulty_bytes.len - 4;
|
||||
for (difficulty_bytes[0..split_idx]) |byte| {
|
||||
if (byte != 0) return error.DifficultyTooLarge;
|
||||
}
|
||||
|
||||
@memcpy(&difficulty_array, difficulty_bytes[split_idx..]);
|
||||
difficulty = std.mem.readInt(u32, &difficulty_array, .big);
|
||||
} else {
|
||||
const start_idx = 4 - difficulty_bytes.len;
|
||||
@memcpy(&difficulty_array, difficulty_bytes[start_idx..]);
|
||||
difficulty = std.mem.readInt(u32, &difficulty_array, .big);
|
||||
}
|
||||
|
||||
next_part = parts.next() orelse return error.InvalidChallenge;
|
||||
}
|
||||
|
||||
var salt = try std.math.big.int.Managed.init(allocator);
|
||||
errdefer salt.deinit();
|
||||
|
||||
const salt_str = next_part;
|
||||
const salt_bytes_len = try std.base64.standard.Decoder.calcSizeForSlice(salt_str);
|
||||
|
||||
std.log.info("salt_bytes_len: {d}\n", .{salt_bytes_len});
|
||||
|
||||
const salt_bytes = try allocator.alloc(u8, salt_bytes_len);
|
||||
defer allocator.free(salt_bytes);
|
||||
|
||||
try std.base64.standard.Decoder.decode(salt_bytes, salt_str);
|
||||
|
||||
std.log.info("decoded salt: {any}\n", .{salt_bytes});
|
||||
|
||||
const usize_salt_bytes: []align(1) usize = std.mem.bytesAsSlice(usize, salt_bytes);
|
||||
// TODO: the bytes are being read in as little endian, but need to be read in as big endian
|
||||
std.log.info("usize_salt_bytes: {any}\n", .{usize_salt_bytes});
|
||||
try salt.ensureCapacity(usize_salt_bytes.len);
|
||||
@memcpy(salt.limbs[0..usize_salt_bytes.len], usize_salt_bytes);
|
||||
salt.setLen(usize_salt_bytes.len);
|
||||
|
||||
const challenge_ptr = try allocator.create(Challenge);
|
||||
errdefer challenge_ptr.destroy(allocator);
|
||||
|
||||
challenge_ptr.* = Challenge{
|
||||
.difficulty = difficulty,
|
||||
.salt = salt,
|
||||
};
|
||||
|
||||
return challenge_ptr;
|
||||
}
|
||||
|
||||
pub fn solve(allocator: Allocator, challenge: *Challenge) ![]u8 {
|
||||
if (challenge.difficulty == null) {
|
||||
return error.InvalidChallenge;
|
||||
}
|
||||
|
||||
for (0..challenge.difficulty.?) |_| {
|
||||
std.log.info("Solving challenge with difficulty {d}\n", .{challenge.difficulty.?});
|
||||
for (0..1277) |_| {
|
||||
try square_mod(&challenge.salt);
|
||||
}
|
||||
try challenge.salt.bitXor(&challenge.salt, &managed_one.?);
|
||||
}
|
||||
|
||||
std.log.info("solved challenge: {any}\n", .{challenge});
|
||||
|
||||
return try challenge.encode(allocator);
|
||||
}
|
||||
|
||||
pub fn check(allocator: Allocator, challenge: *Challenge, solution: *Challenge) !bool {
|
||||
std.log.info("{d}", .{challenge.difficulty.?});
|
||||
std.log.info("{any} vs {any}\n", .{ challenge, solution });
|
||||
|
||||
if (challenge.difficulty == null) {
|
||||
return error.InvalidChallenge;
|
||||
}
|
||||
|
||||
if (managed_one == null) {
|
||||
managed_one = try Int.init(allocator);
|
||||
try managed_one.?.set(1);
|
||||
}
|
||||
|
||||
for (0..challenge.difficulty.?) |_| {
|
||||
try solution.salt.bitXor(&solution.salt, &managed_one.?);
|
||||
|
||||
try square_mod(&solution.salt);
|
||||
}
|
||||
|
||||
std.log.info("{any} vs {any}\n", .{ challenge, solution });
|
||||
|
||||
if (challenge.salt.eql(solution.salt)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
var foo = try std.math.big.int.Managed.initSet(allocator, 2);
|
||||
defer foo.deinit();
|
||||
try foo.pow(&foo, 1279);
|
||||
try foo.sub(&foo, &managed_one.?);
|
||||
try foo.sub(&foo, &challenge.salt);
|
||||
|
||||
if (foo.eql(solution.salt)) {
|
||||
std.log.info("challenge solved!\n", .{});
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1,23 +1,26 @@
|
||||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
const hasher = @import("hasher.zig");
|
||||
const kCTF = @import("kctf.zig");
|
||||
|
||||
// var gpa: std.heap.GeneralPurposeAllocator(.{}) = .init;
|
||||
// var allocator = gpa.allocator();
|
||||
var gpa: std.heap.GeneralPurposeAllocator(.{}) = .init;
|
||||
var allocator = gpa.allocator();
|
||||
|
||||
var allocator = std.heap.wasm_allocator;
|
||||
extern fn __log(str_ptr: usize, str_len: usize) void;
|
||||
|
||||
extern fn __get_solution() i32;
|
||||
extern fn __set_solution(value: i32) void;
|
||||
extern fn __cmpxchg_solution(old: i32, new: i32) i32;
|
||||
extern fn __fetch_add_nonce(value: i32) i32;
|
||||
fn log(comptime level: std.log.Level, comptime scope: @TypeOf(.EnumLiteral), comptime fmt: []const u8, args: anytype) void {
|
||||
if (comptime builtin.target.cpu.arch != .wasm32) {
|
||||
std.log.defaultLog(level, scope, fmt, args);
|
||||
return;
|
||||
}
|
||||
|
||||
// fn log(comptime fmt: []const u8, args: anytype) void {
|
||||
// const formatted = std.fmt.allocPrint(allocator, fmt, args) catch return;
|
||||
// __log(@intFromPtr(formatted.ptr), formatted.len);
|
||||
// allocator.free(formatted);
|
||||
// }
|
||||
const formatted = std.fmt.allocPrint(allocator, fmt, args) catch return;
|
||||
__log(@intFromPtr(formatted.ptr), formatted.len);
|
||||
allocator.free(formatted);
|
||||
}
|
||||
|
||||
pub const std_options: std.Options = .{ .logFn = log };
|
||||
|
||||
export fn malloc(byte_count: usize) ?*u8 {
|
||||
const ptr = allocator.alloc(u8, byte_count) catch return null;
|
||||
@@ -31,129 +34,55 @@ export fn free(ptr: ?*anyopaque, byte_count: usize) void {
|
||||
}
|
||||
}
|
||||
|
||||
const SolveError = enum(u32) {
|
||||
InvalidDifficulty = 1,
|
||||
InvalidNonce = 2,
|
||||
NoSolution = 3,
|
||||
OutOfMemory = 4,
|
||||
};
|
||||
// value_ptr is a string to the entire encoded challenge string (e.g. "s.xxxxxxxxx.xxxxxxx")
|
||||
export fn solve(value_ptr: [*]u8, value_len: usize) usize {
|
||||
const challenge_slice = value_ptr[0..value_len];
|
||||
|
||||
var solve_error: ?SolveError = null;
|
||||
export fn get_solve_error() u32 {
|
||||
if (solve_error) |err| {
|
||||
return @intFromEnum(err);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
std.log.info("Solve called with challenge {s}\n", .{challenge_slice});
|
||||
|
||||
// returns nonce on success, -1 on failure
|
||||
// to get the error, call get_solve_error
|
||||
export fn solve_leaading_zeroes_challenge(challenge_ptr: [*]u8, challenge_len: usize, difficulty: u32) i32 {
|
||||
solve_error = null;
|
||||
|
||||
const challenge_slice = challenge_ptr[0..challenge_len];
|
||||
|
||||
if (difficulty < 1 or difficulty > 64) {
|
||||
solve_error = SolveError.InvalidDifficulty;
|
||||
return -1;
|
||||
}
|
||||
|
||||
var target_prefix_buffer: [64]u8 = @splat('0');
|
||||
const target_prefix = target_prefix_buffer[0..difficulty];
|
||||
|
||||
const max_nonce_iterations: u64 = 1_000_000_000;
|
||||
|
||||
// 64 + 9 digits for nonce since the max nonce is 999_999_999 (not 1 billion since nonce < max_nonce_iterations)
|
||||
var input_buffer: []u8 = allocator.alloc(u8, challenge_len + 9) catch {
|
||||
// log("Failed to allocate memory for challenge\n", .{});
|
||||
solve_error = SolveError.OutOfMemory;
|
||||
return -1;
|
||||
const challenge = kCTF.decode(allocator, challenge_slice) catch |err| {
|
||||
std.log.info("Error decoding challenge: {s}\n", .{@errorName(err)});
|
||||
return 0;
|
||||
};
|
||||
// dont leak memory :pepega:
|
||||
defer allocator.free(input_buffer);
|
||||
defer challenge.destroy(allocator);
|
||||
|
||||
@memcpy(input_buffer[0..challenge_len], challenge_slice);
|
||||
|
||||
var nonce = __fetch_add_nonce(1);
|
||||
|
||||
while (nonce < max_nonce_iterations) : (nonce = __fetch_add_nonce(1)) {
|
||||
if (__get_solution() != -1) {
|
||||
// solution has already been found, no point in continuing
|
||||
return 0;
|
||||
}
|
||||
|
||||
const nonce_str = std.fmt.bufPrint(input_buffer[challenge_len..], "{d}", .{nonce}) catch {
|
||||
solve_error = SolveError.InvalidNonce;
|
||||
return -1;
|
||||
};
|
||||
|
||||
const hash_hex_slice = hasher.hash(allocator, input_buffer[0..challenge_len], input_buffer[challenge_len .. challenge_len + nonce_str.len]) catch {
|
||||
solve_error = SolveError.OutOfMemory;
|
||||
return -1;
|
||||
};
|
||||
|
||||
if (std.mem.startsWith(u8, hash_hex_slice, target_prefix)) {
|
||||
// Found a solution!
|
||||
if (__cmpxchg_solution(-1, nonce) == -1) {
|
||||
// we found a solution, and we are the first to do so
|
||||
return nonce;
|
||||
} else {
|
||||
// we found a solution, but we are not the first to do so
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
solve_error = SolveError.NoSolution;
|
||||
return -1;
|
||||
}
|
||||
|
||||
export fn solve_target_number_challenge(target_ptr: [*]u8, target_len: usize, salt_ptr: [*]u8, salt_len: usize) i32 {
|
||||
solve_error = null;
|
||||
|
||||
const target_slice = target_ptr[0..target_len];
|
||||
const salt_slice = salt_ptr[0..salt_len];
|
||||
|
||||
// TODO: take in max number
|
||||
const max_nonce_iterations: usize = 1_000_000_000;
|
||||
const max_digits = std.math.log10(max_nonce_iterations);
|
||||
|
||||
var input_buffer: []u8 = allocator.alloc(u8, salt_len + max_digits) catch {
|
||||
solve_error = SolveError.OutOfMemory;
|
||||
return -1;
|
||||
std.log.info("decoded challenge {any}\n", .{challenge});
|
||||
const solution = kCTF.solve(allocator, challenge) catch |err| {
|
||||
std.log.info("Error solving challenge: {s}\n", .{@errorName(err)});
|
||||
return 0;
|
||||
};
|
||||
defer allocator.free(input_buffer);
|
||||
|
||||
@memcpy(input_buffer[0..salt_len], salt_slice);
|
||||
std.log.info("Solution: {s}\n", .{solution});
|
||||
|
||||
var nonce = __fetch_add_nonce(1);
|
||||
while (nonce < max_nonce_iterations) : (nonce = __fetch_add_nonce(1)) {
|
||||
if (__get_solution() != -1) {
|
||||
return 0;
|
||||
}
|
||||
const output_ptr = allocator.alloc(u8, solution.len + 4) catch return 0;
|
||||
|
||||
const nonce_str = std.fmt.bufPrint(input_buffer[salt_len..], "{d}", .{nonce}) catch {
|
||||
solve_error = SolveError.InvalidNonce;
|
||||
return -1;
|
||||
};
|
||||
var output_slice = output_ptr[0 .. solution.len + 4];
|
||||
if (output_slice.len - 2 > std.math.maxInt(u16)) {
|
||||
return 0;
|
||||
}
|
||||
const output_len: u16 = @intCast(output_slice.len - 2);
|
||||
// convert to little endian
|
||||
output_slice[0] = @intCast(output_len & 0xFF); // LSB
|
||||
output_slice[1] = @intCast(output_len >> 8); // MSB
|
||||
|
||||
const hash_hex_slice = hasher.hash(allocator, input_buffer[0..salt_len], input_buffer[salt_len .. salt_len + nonce_str.len]) catch {
|
||||
solve_error = SolveError.OutOfMemory;
|
||||
return -1;
|
||||
};
|
||||
@memcpy(output_slice[2..4], "s.");
|
||||
@memcpy(output_slice[4 .. 4 + solution.len], solution);
|
||||
allocator.free(solution);
|
||||
|
||||
if (std.mem.eql(u8, target_slice, hash_hex_slice)) {
|
||||
// Found a solution!
|
||||
if (__cmpxchg_solution(-1, nonce) == -1) {
|
||||
// we found a solution, and we are the first to do so
|
||||
return nonce;
|
||||
} else {
|
||||
// we found a solution, but we are not the first to do so
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return @intFromPtr(output_ptr.ptr);
|
||||
}
|
||||
|
||||
pub fn main() anyerror!void {
|
||||
if (comptime builtin.cpu.arch == .wasm32) return;
|
||||
|
||||
var args = try std.process.argsAlloc(allocator);
|
||||
if (args.len < 2) {
|
||||
std.log.err("Usage: zig run src/kctf.zig <challenge>", .{});
|
||||
return;
|
||||
}
|
||||
|
||||
solve_error = SolveError.NoSolution;
|
||||
return -1;
|
||||
const challenge = try kCTF.decode(allocator, args[1]);
|
||||
const solution = try kCTF.solve(allocator, challenge);
|
||||
|
||||
std.log.info("Solution: {s}", .{solution});
|
||||
}
|
||||
|
||||
@@ -1,9 +1,26 @@
|
||||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
const hasher = @import("hasher.zig");
|
||||
const kCTF = @import("kctf.zig");
|
||||
|
||||
var allocator = std.heap.wasm_allocator;
|
||||
var gpa: std.heap.GeneralPurposeAllocator(.{}) = .init;
|
||||
var allocator = gpa.allocator();
|
||||
|
||||
extern fn __log(str_ptr: usize, str_len: usize) void;
|
||||
|
||||
fn log(comptime level: std.log.Level, comptime scope: @TypeOf(.EnumLiteral), comptime fmt: []const u8, args: anytype) void {
|
||||
if (comptime builtin.target.cpu.arch != .wasm32) {
|
||||
std.log.defaultLog(level, scope, fmt, args);
|
||||
return;
|
||||
}
|
||||
|
||||
const formatted = std.fmt.allocPrint(allocator, fmt, args) catch return;
|
||||
__log(@intFromPtr(formatted.ptr), formatted.len);
|
||||
allocator.free(formatted);
|
||||
}
|
||||
|
||||
pub const std_options: std.Options = .{ .logFn = log };
|
||||
|
||||
export fn malloc(byte_count: usize) ?*u8 {
|
||||
const ptr = allocator.alloc(u8, byte_count) catch return null;
|
||||
@@ -26,56 +43,51 @@ fn bytesToHex(bytes: []const u8, buf: []u8) void {
|
||||
}
|
||||
}
|
||||
|
||||
export fn validate_leading_zeroes_challenge(challenge_ptr: [*]u8, challenge_len: usize, nonce_ptr: [*]u8, nonce_len: usize, difficulty: u32) i32 {
|
||||
const challenge_slice = challenge_ptr[0..challenge_len];
|
||||
const nonce_slice = nonce_ptr[0..nonce_len];
|
||||
// challenge_ptr should look like s.<difficulty>.<challenge>
|
||||
// solution_ptr should look like s.<solved_hash>
|
||||
export fn validate(challenge_ptr: [*]u8, challenge_len: usize, solution_ptr: [*]u8, solution_len: usize) bool {
|
||||
const challenge_buf = challenge_ptr[0..challenge_len];
|
||||
const solution_buf = solution_ptr[0..solution_len];
|
||||
|
||||
if (difficulty < 1 or difficulty > 64) {
|
||||
return -1;
|
||||
std.log.info("Validate called with challenge {s} and solution {s}\n", .{ challenge_buf, solution_buf });
|
||||
|
||||
const challenge = kCTF.decode(allocator, challenge_buf) catch return false;
|
||||
std.log.info("decoded challenge {any}\n", .{challenge});
|
||||
const solution = kCTF.decode(allocator, solution_buf) catch return false;
|
||||
defer {
|
||||
challenge.destroy(allocator);
|
||||
solution.destroy(allocator);
|
||||
}
|
||||
|
||||
var target_prefix_buffer: [64]u8 = @splat('0');
|
||||
const target_prefix = target_prefix_buffer[0..difficulty];
|
||||
std.log.info("decoded challenge and solution\n", .{});
|
||||
|
||||
const hash_hex_slice = hasher.hash(allocator, challenge_slice, nonce_slice) catch return -2;
|
||||
const is_valid = kCTF.check(allocator, challenge, solution) catch return false;
|
||||
|
||||
if (!std.mem.startsWith(u8, hash_hex_slice, target_prefix)) {
|
||||
return -3;
|
||||
return is_valid;
|
||||
}
|
||||
|
||||
pub fn main() anyerror!void {
|
||||
if (comptime builtin.cpu.arch == .wasm32) return;
|
||||
|
||||
const args = try std.process.argsAlloc(allocator);
|
||||
if (args.len < 3) {
|
||||
std.log.err("Usage: zig run src/validator.zig <challenge> <solution>", .{});
|
||||
return;
|
||||
}
|
||||
|
||||
return 0;
|
||||
const challenge = try kCTF.decode(allocator, args[1]);
|
||||
defer challenge.destroy(allocator);
|
||||
|
||||
const solution = try kCTF.decode(allocator, args[2]);
|
||||
defer solution.destroy(allocator);
|
||||
|
||||
std.log.info("Challenge: {any}\n", .{challenge});
|
||||
std.log.info("Solution: {any}\n", .{solution});
|
||||
|
||||
const is_valid = kCTF.check(allocator, challenge, solution) catch |err| {
|
||||
std.log.info("Error checking challenge: {s}\n", .{@errorName(err)});
|
||||
return;
|
||||
};
|
||||
|
||||
std.log.info("Is valid: {}\n", .{is_valid});
|
||||
}
|
||||
|
||||
export fn validate_target_number_challenge(target_ptr: [*]u8, target_len: usize, nonce_ptr: [*]u8, nonce_len: usize, salt_ptr: [*]u8, salt_len: usize) i32 {
|
||||
const target_slice = target_ptr[0..target_len];
|
||||
const salt_slice = salt_ptr[0..salt_len];
|
||||
const nonce_slice = nonce_ptr[0..nonce_len];
|
||||
|
||||
const hash_hex_slice = hasher.hash(allocator, salt_slice, nonce_slice) catch return -2;
|
||||
|
||||
if (!std.mem.eql(u8, target_slice, hash_hex_slice)) {
|
||||
return -3;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
export fn hash(challenge_ptr: [*]u8, challenge_len: usize, nonce_ptr: [*]u8, nonce_len: usize) u64 {
|
||||
const challenge = challenge_ptr[0..challenge_len];
|
||||
const nonce = nonce_ptr[0..nonce_len];
|
||||
const hash_slice = hasher.hash(allocator, challenge, nonce) catch return 0;
|
||||
|
||||
// bs to get the compiler to not whine about hash_slice.len being a u5 annd thus cannot be shifted by 32
|
||||
var ret: u64 = hash_slice.len;
|
||||
ret <<= 32;
|
||||
ret |= @intFromPtr(hash_slice.ptr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// pub fn main() void {
|
||||
// const challenge = "4d7220e22a1ea588fea60000ab8874194e4c6ffd71077adbae915826c73dbf48";
|
||||
// const nonce = "4302";
|
||||
// const difficulty = 3;
|
||||
|
||||
// std.log.info("{d}", .{validate_challenge(@constCast(challenge[0..].ptr), challenge.len, @constCast(nonce[0..].ptr), nonce.len, difficulty)});
|
||||
// }
|
||||
|
||||
Reference in New Issue
Block a user