From bfa0ae484d6553aa3773c7591e85b0fcf1cb65ce Mon Sep 17 00:00:00 2001 From: zzstoatzz Date: Wed, 27 May 2026 00:53:53 -0500 Subject: [PATCH] wire webauthn dependency --- build.zig | 5 +++++ build.zig.zon | 4 ++++ src/internal/passkeys.zig | 46 +++++++++++++++++++++++++++++++++++++++ src/root.zig | 1 + 4 files changed, 56 insertions(+) create mode 100644 src/internal/passkeys.zig diff --git a/build.zig b/build.zig index e2abedc..c349859 100644 --- a/build.zig +++ b/build.zig @@ -13,6 +13,10 @@ pub fn build(b: *std.Build) void { .target = target, .optimize = optimize, }); + const webauthn = b.dependency("webauthn", .{ + .target = target, + .optimize = optimize, + }); const mod = b.addModule("zds", .{ .root_source_file = b.path("src/root.zig"), @@ -22,6 +26,7 @@ pub fn build(b: *std.Build) void { .{ .name = "build_options", .module = buildOptions(b, version) }, .{ .name = "zat", .module = zat.module("zat") }, .{ .name = "zqlite", .module = zqlite.module("zqlite") }, + .{ .name = "webauthn", .module = webauthn.module("webauthn") }, }, }); diff --git a/build.zig.zon b/build.zig.zon index 4cd5cdf..480fc9b 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -12,6 +12,10 @@ .url = "git+https://github.com/karlseguin/zqlite.zig?ref=master#05a88d6758753e1c63fdd45b211dde2057094b0c", .hash = "zqlite-0.0.1-RWLaYz6bmAAT7E_jxopXf-j5Ea8VQldnxsd6TU8sa0Bb", }, + .webauthn = .{ + .url = "git+https://tangled.org/zzstoatzz.io/webauthn?ref=main#252f7f33ba63bfb21911edecb0be4773f6432883", + .hash = "webauthn-0.0.1--JitMhxtAACEz8AIen39HWsjUPDMx1ync0TA99tpU2q5", + }, }, .paths = .{ "build.zig", diff --git a/src/internal/passkeys.zig b/src/internal/passkeys.zig new file mode 100644 index 0000000..9c3825f --- /dev/null +++ b/src/internal/passkeys.zig @@ -0,0 +1,46 @@ +const std = @import("std"); +const webauthn = @import("webauthn"); + +pub const CredentialKey = struct { + bytes: []const u8, + + pub fn validate(self: CredentialKey) !void { + _ = try webauthn.cose.parseEc2PublicKey(self.bytes); + } +}; + +pub fn buildAssertionMessage(allocator: std.mem.Allocator, authenticator_data: []const u8, client_data_json: []const u8) ![]u8 { + var client_hash: [32]u8 = undefined; + std.crypto.hash.sha2.Sha256.hash(client_data_json, &client_hash, .{}); + + const out = try allocator.alloc(u8, authenticator_data.len + client_hash.len); + @memcpy(out[0..authenticator_data.len], authenticator_data); + @memcpy(out[authenticator_data.len..], &client_hash); + return out; +} + +pub fn verifyAssertionSignature(credential_public_key: []const u8, signature_der: []const u8, authenticator_data: []const u8, client_data_json: []const u8, allocator: std.mem.Allocator) !void { + const message = try buildAssertionMessage(allocator, authenticator_data, client_data_json); + defer allocator.free(message); + try webauthn.crypto.verifyWebAuthnEs256(credential_public_key, signature_der, message); +} + +test "validates webauthn credential key through tangled dependency" { + const key = try webauthn.base64url.decodeAlloc(std.testing.allocator, "pQECAyYgASFYIDNDxl6djmZTEhKfw1B5jiSdcFUsTKuyPpks-4jTpA5aIlggF5oAEvUgwjYE6o0sPzL6G27d72m3lM2-yPAMOajmYoE"); + defer std.testing.allocator.free(key); + try (CredentialKey{ .bytes = key }).validate(); +} + +test "verifies real webauthn assertion signature through zds adapter" { + const allocator = std.testing.allocator; + const key = try webauthn.base64url.decodeAlloc(allocator, "pQECAyYgASFYIDNDxl6djmZTEhKfw1B5jiSdcFUsTKuyPpks-4jTpA5aIlggF5oAEvUgwjYE6o0sPzL6G27d72m3lM2-yPAMOajmYoE"); + defer allocator.free(key); + const auth_data = try webauthn.base64url.decodeAlloc(allocator, "SZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2MdAAAAAA"); + defer allocator.free(auth_data); + const client_data_json = try webauthn.base64url.decodeAlloc(allocator, "eyJ0eXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoibGgwR1c2OEZKZW03NWxBNV9sRTZKTmU4dlo2ODdsdmhaQmtrY0RzUVB5byIsIm9yaWdpbiI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODA4MCIsImNyb3NzT3JpZ2luIjpmYWxzZX0"); + defer allocator.free(client_data_json); + const signature = try webauthn.base64url.decodeAlloc(allocator, "MEYCIQDQ-pXZQT9yjPsXT_m47W-iTFAIRgBVOCBhwl6kU--0RwIhAKcJJhxipw6tsIR0ULRgvQAhTaeIXk_V29wKOqbfP1oL"); + defer allocator.free(signature); + + try verifyAssertionSignature(key, signature, auth_data, client_data_json, allocator); +} diff --git a/src/root.zig b/src/root.zig index 95a9db3..cba831d 100644 --- a/src/root.zig +++ b/src/root.zig @@ -31,6 +31,7 @@ pub const http = struct { pub const internal = struct { pub const cli = @import("internal/cli.zig"); + pub const passkeys = @import("internal/passkeys.zig"); pub const sharded_locks = @import("internal/sharded_locks.zig"); }; -- 2.51.2