From fca98a06afdd0626cd95678db2fb67805be2651e Mon Sep 17 00:00:00 2001 From: David Sugar Date: Tue, 13 May 2025 09:12:01 +0000 Subject: [PATCH 01/17] Create LICENSE Add missing LICENSE --- LICENSE | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d6eccc0 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 David P. Sugar + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. From 324d8a5109678509774192f90ccbd8857e897fec Mon Sep 17 00:00:00 2001 From: "David P. Sugar" Date: Sat, 16 Aug 2025 18:28:13 +0200 Subject: [PATCH 02/17] updated kdbx package version and bumped up own package version --- build.zig.zon | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/build.zig.zon b/build.zig.zon index 4f40045..c4ddb95 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -1,16 +1,12 @@ .{ .name = .passkeez, - .version = "0.5.2", + .version = "0.5.3", .fingerprint = 0xdd1692d15d21a6f6, .dependencies = .{ .keylib = .{ .url = "https://github.com/Zig-Sec/keylib/archive/refs/tags/0.6.1.tar.gz", .hash = "keylib-0.6.1-mbYjk9-WCQBOqB9oq2ZqWQFeWI2HwuBEuPgL-wkJDHTg", }, - .kdbx = .{ - .url = "https://github.com/Zig-Sec/kdbx/archive/refs/tags/0.1.2.tar.gz", - .hash = "kdbx-0.1.2-eJXZBIWuCgCzDIuSdWaJ1OV41OtouFv3XhdrOn23XnzK", - }, .uuid = .{ .url = "https://github.com/r4gus/uuid-zig/archive/refs/tags/0.3.1.tar.gz", .hash = "uuid-0.3.0-oOieIYF1AAA_BtE7FvVqqTn5uEYTvvz7ycuVnalCOf8C", @@ -19,6 +15,10 @@ .url = "https://github.com/r4gus/ccdb/archive/refs/tags/0.3.1.tar.gz", .hash = "ccdb-0.3.2-mrOGYn4KDgC0cJl4CKpKaJB95wwIlHegHLV7NSbMYXu0", }, + .kdbx = .{ + .url = "https://github.com/Zig-Sec/kdbx/archive/refs/tags/0.1.4.tar.gz", + .hash = "kdbx-0.1.4-eJXZBJG9CgDqY3mcSIxof3vyuZFTaxW2ZIDRPOiryWVw", + }, }, .paths = .{ "build.zig", From 671000f8ad4ab5379b396e9bc2f6eef39fb41c90 Mon Sep 17 00:00:00 2001 From: "David P. Sugar" Date: Sun, 17 Aug 2025 00:40:48 +0200 Subject: [PATCH 03/17] README updated --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 69d7c2e..644b342 100644 --- a/README.md +++ b/README.md @@ -22,11 +22,11 @@ The project currently supports only Linux due to the absence of a standardized A ## Features * Works with all services that support Passkeys. -* Store your Passkeys (just a private key + related data) in a local, encrypted database. Choose either the KDBX (KeePassXC, ...) or CCDB database format. +* Store your Passkeys (just a private key + related data) in a local, encrypted database. * Constant sign-counter, i.e. you can safely sync your credentials/passkeys between devices. > [!IMPORTANT] -> KDBX support has been added with version 0.5.0. The advantage of using KDBX is that you can manage your passkeys using KeePass or KeePassXC (PassKeeZ uses the same format for storing passkeys as KeePassXC). If you run into issues please open an issue. +> With the release of version 0.5.0, PassKeeZ uses the KDBX format for storing credentials. The advantage of using KDBX is that you can manage your passkeys using KeePass or KeePassXC (PassKeeZ uses the same format for storing passkeys as KeePassXC). If you run into issues please open an issue. ## Getting Started @@ -34,7 +34,7 @@ To get started please visit the [wiki](https://github.com/Zig-Sec/PassKeeZ/wiki/ ### Database Management -- KDBX: You can manage your `.kdbx` database with [KeePassXC](https://keepassxc.org/). +- KDBX: You can manage your `.kdbx` database with [KeePassXC](https://keepassxc.org/) or KeePass. - CCDB: Currently the only way to manage your Credentials is by using the [CCDB command line application](https://github.com/r4gus/ccdb). ### File synchronization From 8e08d61276838691b1004f563b2721f91f97d779 Mon Sep 17 00:00:00 2001 From: "David P. Sugar" Date: Sun, 17 Aug 2025 00:57:49 +0200 Subject: [PATCH 04/17] updated default PassKeeZ version of install script --- script/install-linux.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/install-linux.sh b/script/install-linux.sh index 88a7482..7b005de 100755 --- a/script/install-linux.sh +++ b/script/install-linux.sh @@ -1,6 +1,6 @@ #!/bin/bash -PASSKEEZ_VERSION="0.5.2" +PASSKEEZ_VERSION="0.5.3" ZIGENITY_VERSION="0.5.0" ZIG_VERSION="0.14.0" From 8c5d0fd23c834a033251cfa79bce882b16983761 Mon Sep 17 00:00:00 2001 From: "David P. Sugar" Date: Sat, 23 Aug 2025 23:29:04 +0200 Subject: [PATCH 05/17] fixed package hash --- build.zig.zon | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/build.zig.zon b/build.zig.zon index c4ddb95..cf7230d 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -17,7 +17,7 @@ }, .kdbx = .{ .url = "https://github.com/Zig-Sec/kdbx/archive/refs/tags/0.1.4.tar.gz", - .hash = "kdbx-0.1.4-eJXZBJG9CgDqY3mcSIxof3vyuZFTaxW2ZIDRPOiryWVw", + .hash = "kdbx-0.1.4-eJXZBMa7CgAIw7kG02QYyv1sFcePsjfoGH9-Ptl3wrTC", }, }, .paths = .{ @@ -29,4 +29,12 @@ "src", "static", }, + .licenses = .{ + .{ + .expression = "MIT", + .paths = .{ + "", + }, + }, + }, } From d197a1b88d89beffb32c9de53eeb731782374b27 Mon Sep 17 00:00:00 2001 From: "David P. Sugar" Date: Tue, 16 Sep 2025 14:02:55 +0200 Subject: [PATCH 06/17] updated to Zig version 0.15.1 --- build.zig | 26 ++- build.zig.zon | 26 +-- src/Database.zig | 1 - src/cred_mgmt.zig | 32 ++-- src/database/Config.zig | 6 +- src/database/ccdb.zig | 387 ---------------------------------------- src/database/kdbx.zig | 40 ++--- src/database/misc.zig | 3 +- src/main.zig | 10 +- src/state.zig | 38 ++-- 10 files changed, 94 insertions(+), 475 deletions(-) delete mode 100644 src/database/ccdb.zig diff --git a/build.zig b/build.zig index 8f1ab4b..948572b 100644 --- a/build.zig +++ b/build.zig @@ -9,11 +9,6 @@ pub fn build(b: *std.Build) void { .optimize = optimize, }); - const ccdb_dep = b.dependency("ccdb", .{ - .target = target, - .optimize = optimize, - }); - const kdbx_dep = b.dependency("kdbx", .{ .target = target, .optimize = optimize, @@ -26,16 +21,19 @@ pub fn build(b: *std.Build) void { const exe = b.addExecutable(.{ .name = "passkeez", - .root_source_file = b.path("src/main.zig"), - .target = target, - .optimize = optimize, + .root_module = b.createModule(.{ + .target = target, + .optimize = optimize, + .root_source_file = b.path("src/main.zig"), + .imports = &.{ + .{ .name = "keylib", .module = keylib_dep.module("keylib") }, + .{ .name = "uhid", .module = keylib_dep.module("uhid") }, + .{ .name = "zbor", .module = keylib_dep.module("zbor") }, + .{ .name = "kdbx", .module = kdbx_dep.module("kdbx") }, + .{ .name = "uuid", .module = uuid_dep.module("uuid") }, + }, + }), }); - exe.root_module.addImport("keylib", keylib_dep.module("keylib")); - exe.root_module.addImport("uhid", keylib_dep.module("uhid")); - exe.root_module.addImport("zbor", keylib_dep.module("zbor")); - exe.root_module.addImport("ccdb", ccdb_dep.module("ccdb")); - exe.root_module.addImport("kdbx", kdbx_dep.module("kdbx")); - exe.root_module.addImport("uuid", uuid_dep.module("uuid")); exe.linkLibC(); b.installArtifact(exe); diff --git a/build.zig.zon b/build.zig.zon index cf7230d..a1b71dc 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -1,23 +1,21 @@ .{ .name = .passkeez, .version = "0.5.3", + .minimum_zig_version = "0.15.1", .fingerprint = 0xdd1692d15d21a6f6, .dependencies = .{ .keylib = .{ - .url = "https://github.com/Zig-Sec/keylib/archive/refs/tags/0.6.1.tar.gz", - .hash = "keylib-0.6.1-mbYjk9-WCQBOqB9oq2ZqWQFeWI2HwuBEuPgL-wkJDHTg", + .url = "https://github.com/Zig-Sec/keylib/archive/refs/tags/0.7.0.tar.gz", + .hash = "keylib-0.7.0-mbYjk6qaCQACutrMpyhgstSmYxSKmcuRmLI-CJSumBeA", }, .uuid = .{ - .url = "https://github.com/r4gus/uuid-zig/archive/refs/tags/0.3.1.tar.gz", - .hash = "uuid-0.3.0-oOieIYF1AAA_BtE7FvVqqTn5uEYTvvz7ycuVnalCOf8C", - }, - .ccdb = .{ - .url = "https://github.com/r4gus/ccdb/archive/refs/tags/0.3.1.tar.gz", - .hash = "ccdb-0.3.2-mrOGYn4KDgC0cJl4CKpKaJB95wwIlHegHLV7NSbMYXu0", + .url = "https://github.com/r4gus/uuid-zig/archive/refs/tags/0.4.0.tar.gz", + .hash = "uuid-0.4.0-oOieIR2AAAChAUVBY4ABjYI1XN0EbVALmiN0JIlggC3i", }, .kdbx = .{ - .url = "https://github.com/Zig-Sec/kdbx/archive/refs/tags/0.1.4.tar.gz", - .hash = "kdbx-0.1.4-eJXZBMa7CgAIw7kG02QYyv1sFcePsjfoGH9-Ptl3wrTC", + //.url = "https://github.com/Zig-Sec/kdbx/archive/refs/tags/0.2.0.tar.gz", + //.hash = "kdbx-0.2.0-eJXZBNecEgC8JhA9_2_EfGy-4cvbEDQAITLXyrxdKACv", + .path = "../kdbx", }, }, .paths = .{ @@ -29,12 +27,4 @@ "src", "static", }, - .licenses = .{ - .{ - .expression = "MIT", - .paths = .{ - "", - }, - }, - }, } diff --git a/src/Database.zig b/src/Database.zig index 3edb354..d1b47f9 100644 --- a/src/Database.zig +++ b/src/Database.zig @@ -2,7 +2,6 @@ const std = @import("std"); const keylib = @import("keylib"); const Credential = keylib.ctap.authenticator.Credential; -pub const ccdb = @import("database/ccdb.zig"); pub const kdbx = @import("database/kdbx.zig"); const Self = @This(); diff --git a/src/cred_mgmt.zig b/src/cred_mgmt.zig index b133976..575f5d1 100644 --- a/src/cred_mgmt.zig +++ b/src/cred_mgmt.zig @@ -11,17 +11,17 @@ var gpa = std.heap.GeneralPurposeAllocator(.{}){}; pub fn authenticatorCredentialManagement( auth: *keylib.ctap.authenticator.Auth, request: []const u8, - out: *std.ArrayList(u8), + out: *std.Io.Writer, ) keylib.ctap.StatusCodes { const S = struct { const max = 1000; var i: i64 = 0; - var rps: ?std.ArrayList(keylib.common.RelyingParty) = null; + var rps: ?std.ArrayListUnmanaged(keylib.common.RelyingParty) = null; const allocator = gpa.allocator(); pub fn deinitRps() void { - if (rps) |_rps| { - _rps.deinit(); + if (rps) |*_rps| { + _rps.deinit(allocator); } rps = null; } @@ -66,7 +66,7 @@ pub fn authenticatorCredentialManagement( if (status != .ctap1_err_success) return status; - cbor.stringify(res, .{}, out.writer()) catch { + cbor.stringify(res, .{}, out) catch { std.log.err("credentialManagement: cbor encoding error", .{}); return keylib.ctap.StatusCodes.ctap1_err_other; }; @@ -106,8 +106,8 @@ pub fn enumerateRPsBegin(auth: *keylib.ctap.authenticator.Auth, req: *const Requ }; state.deinitRps(); - state.rps = std.ArrayList(keylib.common.RelyingParty).init(state.allocator); - state.rps.?.append(cred.rp) catch { + state.rps = .empty; + state.rps.?.append(state.allocator, cred.rp) catch { state.deinitRps(); status.* = .ctap1_err_other; return .{}; @@ -124,7 +124,7 @@ pub fn enumerateRPsBegin(auth: *keylib.ctap.authenticator.Auth, req: *const Requ if (std.mem.eql(u8, rp.id.get(), rp2.id.get())) continue :outer; } - state.rps.?.append(rp) catch { + state.rps.?.append(state.allocator, rp) catch { state.deinitRps(); status.* = .ctap1_err_other; return .{}; @@ -219,22 +219,22 @@ pub fn enumerateCredentialsBegin(auth: *keylib.ctap.authenticator.Auth, req: *co return .{}; } - var m = std.ArrayList(u8).init(state.allocator); + var m = std.Io.Writer.Allocating.init(state.allocator); defer m.deinit(); - m.append(0x04) catch { + m.writer.writeByte(0x04) catch { status.* = .ctap1_err_other; return .{}; }; cbor.stringify( req.subCommandParams.?, .{}, - m.writer(), + &m.writer, ) catch { status.* = .ctap1_err_other; return .{}; }; - if (!auth.token.verify_token(m.items, req.pinUvAuthParam.?.get())) { + if (!auth.token.verify_token(m.written(), req.pinUvAuthParam.?.get())) { status.* = .ctap2_err_pin_auth_invalid; return .{}; } @@ -319,22 +319,22 @@ pub fn deleteCredential(auth: *keylib.ctap.authenticator.Auth, req: *const Reque return .{}; } - var m = std.ArrayList(u8).init(state.allocator); + var m = std.Io.Writer.Allocating.init(state.allocator); defer m.deinit(); - m.append(0x06) catch { + m.writer.writeByte(0x06) catch { status.* = .ctap1_err_other; return .{}; }; cbor.stringify( req.subCommandParams.?, .{}, - m.writer(), + &m.writer, ) catch { status.* = .ctap1_err_other; return .{}; }; - if (!auth.token.verify_token(m.items, req.pinUvAuthParam.?.get())) { + if (!auth.token.verify_token(m.written(), req.pinUvAuthParam.?.get())) { status.* = .ctap2_err_pin_auth_invalid; return .{}; } diff --git a/src/database/Config.zig b/src/database/Config.zig index 3c8f002..07be539 100644 --- a/src/database/Config.zig +++ b/src/database/Config.zig @@ -35,13 +35,13 @@ pub fn create(a: std.mem.Allocator) !void { var file = try home_dir.createFile(".passkeez/config.json", .{ .exclusive = true }); defer file.close(); - var str = std.ArrayList(u8).init(a); + var str = std.Io.Writer.Allocating.init(a); defer str.deinit(); const x = @This(){}; - try std.json.stringify(x, .{}, str.writer()); + try std.json.Stringify.value(x, .{}, &str.writer); - try file.writeAll(str.items); + try file.writeAll(str.written()); } pub fn deinit(self: *const @This(), a: std.mem.Allocator) void { diff --git a/src/database/ccdb.zig b/src/database/ccdb.zig deleted file mode 100644 index cbc9b09..0000000 --- a/src/database/ccdb.zig +++ /dev/null @@ -1,387 +0,0 @@ -const std = @import("std"); -const TDatabase = @import("../Database.zig"); -const misc = @import("misc.zig"); -const ccdb = @import("ccdb"); -const keylib = @import("keylib"); -const Credential = keylib.ctap.authenticator.Credential; -const i18n = @import("../i18n.zig"); -const State = @import("../state.zig"); - -pub fn Database( - path: []const u8, - pw: []const u8, - allocator: std.mem.Allocator, -) TDatabase.Error!TDatabase { - return TDatabase{ - .path = allocator.dupe(u8, path) catch return error.OutOfMemory, - .pw = allocator.dupe(u8, pw) catch return error.OutOfMemory, - .allocator = allocator, - .init = init, - .deinit = deinit, - .save = save, - .getCredential = getCredential, - .setCredential = setCredential, - .deleteCredential = deleteCredential, - }; -} - -fn init(self: *TDatabase) TDatabase.Error!void { - var file = misc.openFile(self.path) catch |e| blk: { - if (e == error.WouldBlock) { - std.log.err("Cannot open database: ({any})", .{e}); - return error.WouldBlock; - } else { // FileNotFound - break :blk createDialog(self.allocator, self.path) catch |e2| { - std.log.err("Cannot open database: ({any})", .{e2}); - return error.FileNotFound; - }; - } - }; - defer file.close(); - - const mem = file.readToEndAlloc(self.allocator, 50_000_000) catch return error.FileError; - defer self.allocator.free(mem); - - const db = try self.allocator.create(ccdb.Db); - errdefer self.allocator.destroy(db); - - db.* = ccdb.Db.open( - mem, - self.allocator, - std.time.milliTimestamp, - std.crypto.random, - self.pw, - ) catch return error.DatabaseError; - - self.db = db; -} - -fn deinit(self: *const TDatabase) void { - if (self.db) |db| { - var db_ = @as(*ccdb.Db, @alignCast(@ptrCast(db))); - db_.deinit(); - } - self.allocator.free(self.path); - self.allocator.free(self.pw); -} - -fn save(self: *const TDatabase, a: std.mem.Allocator) TDatabase.Error!void { - var db = @as(*ccdb.Db, @alignCast(@ptrCast(self.db.?))); - const raw = db.seal(a) catch |e| { - std.log.err("Cannot to seal database: {any}", .{e}); - return error.DatabaseError; - }; - defer { - @memset(raw, 0); - a.free(raw); - } - misc.writeFile(self.path, raw, a) catch |e| { - std.log.err("Cannot to save database: {any}", .{e}); - return error.DatabaseError; - }; -} - -fn deleteCredential( - self: *const TDatabase, - id: [36]u8, -) TDatabase.Error!void { - const db = @as(*ccdb.Db, @alignCast(@ptrCast(self.db.?))); - - db.body.deleteEntryById(id) catch |e| { - std.log.err("Cannot to delete entry with id: {s} ({any})", .{ id, e }); - return error.DoesNotExist; - }; - - // persist data - save(self, self.allocator) catch { - return error.Other; - }; -} - -fn getCredential( - self: *const TDatabase, - rp_id: ?[]const u8, - rp_id_hash: ?[32]u8, - idx: *usize, -) TDatabase.Error!Credential { - const db = @as(*ccdb.Db, @alignCast(@ptrCast(self.db.?))); - - while (db.body.entries.len > idx.*) { - const entry = db.body.entries[idx.*]; - idx.* += 1; - - if (rp_id) |rpId| { - if (entry.url) |url| { - if (std.mem.eql(u8, url, rpId)) { - return credentialFromEntry(&entry) catch { - std.log.warn("Entry with id {s} is not a credential ({any})", .{ - entry.uuid[0..], - entry, - }); - continue; - }; - } - } - } else if (rp_id_hash) |hash| { - var digest: [32]u8 = .{0} ** 32; - - if (entry.url) |url| { - std.crypto.hash.sha2.Sha256.hash(url, &digest, .{}); - - if (std.mem.eql(u8, &hash, &digest)) { - return credentialFromEntry(&entry) catch { - std.log.warn("Entry with id {s} is not a credential ({any})", .{ - entry.uuid[0..], - entry, - }); - continue; - }; - } - } - } else { // if no rpId is given: return every entry - return credentialFromEntry(&entry) catch { - std.log.warn("Entry with id {s} is not a credential ({any})", .{ - entry.uuid[0..], - entry, - }); - continue; - }; - } - } - - return error.DoesNotExist; -} - -fn setCredential( - self: *const TDatabase, - data: Credential, -) TDatabase.Error!void { - const db = @as(*ccdb.Db, @alignCast(@ptrCast(self.db.?))); - - var e = if (db.body.getEntryById(data.id.get())) |e| e else blk: { - var e = db.body.newEntry() catch { - std.log.err("unable to create new entry", .{}); - return error.Other; - }; - // We use the uuid generated by keylib as uuid for our entry. - @memcpy(e.uuid[0..], data.id.get()); - break :blk e; - }; - - // update user - e.setUser(.{ - .id = data.user.id.get(), - .name = if (data.user.name) |name| name.get() else null, - .display_name = if (data.user.displayName) |name| name.get() else null, - }) catch { - std.log.err("unable to update name of entry with id {s}", .{data.id.get()}); - return error.Other; - }; - - // update rp - e.setUrl(data.rp.id.get()) catch { - std.log.err("unable to update url of entry with id {s}", .{data.id.get()}); - return error.Other; - }; - - e.times.cnt = data.sign_count; - - e.setKey(data.key); - - if (e.tags) |tags| { - for (tags, 0..) |tag, i| { - if (tag.len < 8) continue; - if (!std.mem.eql(u8, "policy:", tag[0..7])) continue; - - e.removeTag(i); - - break; - } - } - - const p = std.fmt.allocPrint(self.allocator, "policy:{s}", .{ - data.policy.toString(), - }) catch { - std.log.err("unable to allocate memory for policy of entry with id {s}", .{data.id.get()}); - return error.Other; - }; - defer self.allocator.free(p); - - e.addTag(p) catch { - std.log.err("unable to update policy of entry with id {s}", .{data.id.get()}); - return error.Other; - }; - - // persist data - save(self, self.allocator) catch { - return error.Other; - }; -} - -// ----------------- Helper ---------------------- - -pub fn createDialog(allocator: std.mem.Allocator, path: []const u8) !std.fs.File { - const r1 = std.process.Child.run(.{ - .allocator = allocator, - .argv = &.{ - "zigenity", - "--question", - "--window-icon=/usr/share/passkeez/passkeez.png", - "--icon=/usr/share/passkeez/passkeez-question.png", - i18n.get(State.conf.lang).no_database_title, - i18n.get(State.conf.lang).no_database, - }, - }) catch |e| { - std.log.err("unable to execute zigenity ({any})", .{e}); - return error.Other; - }; - - defer { - allocator.free(r1.stdout); - allocator.free(r1.stderr); - } - - switch (r1.term.Exited) { - 0 => {}, - else => return error.CreateDbRejected, - } - - outer: while (true) { - var r2 = std.process.Child.run(.{ - .allocator = allocator, - .argv = &.{ - "zigenity", - "--password", - "--window-icon=/usr/share/passkeez/passkeez.png", - i18n.get(State.conf.lang).new_database_title, - i18n.get(State.conf.lang).new_database, - i18n.get(State.conf.lang).new_database_ok, - "--cancel-label=Cancel", - }, - }) catch |e| { - std.log.err("unable to execute zigenity ({any})", .{e}); - return error.Other; - }; - defer { - allocator.free(r2.stdout); - allocator.free(r2.stderr); - } - - switch (r2.term.Exited) { - 0 => { - std.log.info("{s}", .{r2.stdout}); - const pw1 = r2.stdout[0 .. r2.stdout.len - 1]; - - if (pw1.len < 8) { - const r = std.process.Child.run(.{ - .allocator = allocator, - .argv = &.{ - "zigenity", - "--question", - "--window-icon=/usr/share/passkeez/passkeez.png", - "--icon=/usr/share/passkeez/passkeez-error.png", - "--text=Password must be 8 characters long", - "--title=PassKeeZ: Error", - "--timeout=15", - "--switch-cancel", - "--ok-label=Ok", - }, - }) catch |e| { - std.log.err("unable to execute zigenity ({any})", .{e}); - return error.Other; - }; - defer { - allocator.free(r.stdout); - allocator.free(r.stderr); - } - continue :outer; - } - - const f_db = misc.createFile(path) catch |e| { - std.log.err("Cannot create new database file: {any}", .{e}); - return error.FileError; - }; - errdefer f_db.close(); - - var store = ccdb.Db.new("PassKeeZ", "Passkeys", .{}, allocator) catch |e| { - std.log.err("Cannot create database: {any}", .{e}); - return error.DatabaseError; - }; - defer store.deinit(); - store.setKey(pw1) catch |e| { - std.log.err("Cannot set database key: {any}", .{e}); - return error.DatabaseError; - }; - const raw = store.seal(allocator) catch |e| { - std.log.err("Cannot seal database: {any}", .{e}); - return error.DatabaseError; - }; - defer { - @memset(raw, 0); - allocator.free(raw); - } - - f_db.writer().writeAll(raw) catch |e| { - std.log.err("Cannot write to database: {any}", .{e}); - return error.DatabaseError; - }; - - const r = std.process.Child.run(.{ - .allocator = allocator, - .argv = &.{ - "zigenity", - "--question", - "--window-icon=/usr/share/passkeez/passkeez.png", - "--icon=/usr/share/passkeez/passkeez-ok.png", - i18n.get(State.conf.lang).database_created, - i18n.get(State.conf.lang).database_created_title, - "--timeout=15", - "--switch-cancel", - "--ok-label=Ok", - }, - }) catch |e| { - std.log.err("Cannot execute zigenity: {any}", .{e}); - return error.Other; - }; - defer { - allocator.free(r.stdout); - allocator.free(r.stderr); - } - - return f_db; - }, - else => return error.CreateDbRejected, - } - } -} - -pub fn credentialFromEntry(entry: *const ccdb.Entry) !keylib.ctap.authenticator.Credential { - if (entry.user == null) return error.MissingUser; - if (entry.url == null) return error.MissingRelyingParty; - if (entry.key == null) return error.MissingKey; - if (entry.tags == null) return error.MissingPolicy; - - const policy = blk: for (entry.tags.?) |tag| { - if (tag.len < 8) continue; - if (!std.mem.eql(u8, "policy:", tag[0..7])) continue; - - if (keylib.ctap.extensions.CredentialCreationPolicy.fromString(tag[7..])) |p| { - break :blk p; - } else { - return error.MissingPolicy; - } - } else { - return error.MissingPolicy; - }; - - return .{ - .id = (try keylib.common.dt.ABS64B.fromSlice(entry.uuid[0..])).?, - .user = try keylib.common.User.new(entry.user.?.id.?, entry.user.?.name, entry.user.?.display_name), - .rp = try keylib.common.RelyingParty.new(entry.url.?, null), - .sign_count = if (entry.times.cnt) |cnt| cnt else 0, - .key = entry.key.?, - .created = entry.times.creat, - .discoverable = true, - .policy = policy, - }; -} diff --git a/src/database/kdbx.zig b/src/database/kdbx.zig index 16a4101..6300328 100644 --- a/src/database/kdbx.zig +++ b/src/database/kdbx.zig @@ -44,8 +44,7 @@ fn init(self: *TDatabase) TDatabase.Error!void { const mem = file.readToEndAlloc(self.allocator, 50_000_000) catch return error.FileError; defer self.allocator.free(mem); - var fbs = std.io.fixedBufferStream(mem); - const reader = fbs.reader(); + var reader = std.Io.Reader.fixed(mem); const db = try self.allocator.create(kdbx.Database); errdefer self.allocator.destroy(db); @@ -56,7 +55,7 @@ fn init(self: *TDatabase) TDatabase.Error!void { }; defer db_key.deinit(); - db.* = kdbx.Database.open(reader, .{ + db.* = kdbx.Database.open(&reader, .{ .allocator = self.allocator, .key = db_key, }) catch |e| { @@ -69,7 +68,7 @@ fn init(self: *TDatabase) TDatabase.Error!void { fn deinit(self: *const TDatabase) void { if (self.db) |db| { - var db_ = @as(*kdbx.Database, @alignCast(@ptrCast(db))); + var db_ = @as(*kdbx.Database, @ptrCast(@alignCast(db))); db_.deinit(); } self.allocator.free(self.path); @@ -77,9 +76,9 @@ fn deinit(self: *const TDatabase) void { } fn save(self: *const TDatabase, a: std.mem.Allocator) TDatabase.Error!void { - var db = @as(*kdbx.Database, @alignCast(@ptrCast(self.db.?))); + var db = @as(*kdbx.Database, @ptrCast(@alignCast(self.db.?))); - var raw = std.ArrayList(u8).init(a); + var raw = std.Io.Writer.Allocating.init(a); defer raw.deinit(); const db_key = kdbx.DatabaseKey{ @@ -89,7 +88,7 @@ fn save(self: *const TDatabase, a: std.mem.Allocator) TDatabase.Error!void { defer db_key.deinit(); db.save( - raw.writer(), + &raw.writer, db_key, a, ) catch |e| { @@ -97,7 +96,7 @@ fn save(self: *const TDatabase, a: std.mem.Allocator) TDatabase.Error!void { return error.DatabaseError; }; - misc.writeFile(self.path, raw.items, a) catch |e| { + misc.writeFile(self.path, raw.written(), a) catch |e| { std.log.err("Cannot to save database: {any}", .{e}); return error.DatabaseError; }; @@ -107,13 +106,13 @@ fn deleteCredential( self: *const TDatabase, urn: [36]u8, ) TDatabase.Error!void { - const db = @as(*kdbx.Database, @alignCast(@ptrCast(self.db.?))); + const db = @as(*kdbx.Database, @ptrCast(@alignCast(self.db.?))); const grp = db.body.root.getGroupByName("Passkeys") orelse return; const id = Uuid.urn.deserialize(&urn) catch return; - const e1 = grp.removeEntryByUuid(id); - if (e1) |e1_| e1_.deinit(); + var e1 = grp.removeEntryByUuid(id); + if (e1) |*e1_| e1_.deinit(); // persist data save(self, self.allocator) catch { @@ -127,7 +126,7 @@ fn getCredential( rp_id_hash: ?[32]u8, idx: *usize, ) TDatabase.Error!Credential { - const db: *kdbx.Database = @as(*kdbx.Database, @alignCast(@ptrCast(self.db.?))); + const db: *kdbx.Database = @as(*kdbx.Database, @ptrCast(@alignCast(self.db.?))); const grp = db.body.root.getGroupByName("Passkeys") orelse return error.DatabaseError; while (grp.entries.items.len > idx.*) { @@ -169,7 +168,7 @@ fn setCredential( self: *const TDatabase, data: Credential, ) TDatabase.Error!void { - const db: *kdbx.Database = @as(*kdbx.Database, @alignCast(@ptrCast(self.db.?))); + const db: *kdbx.Database = @as(*kdbx.Database, @ptrCast(@alignCast(self.db.?))); const grp = db.body.root.getGroupByName("Passkeys") orelse return error.DatabaseError; const id = Uuid.urn.deserialize(data.id.get()) catch { @@ -187,8 +186,8 @@ fn setCredential( break :blk e; }; errdefer { - const e_ = grp.removeEntryByUuid(id); - if (e_) |e__| e__.deinit(); + var e_ = grp.removeEntryByUuid(id); + if (e_) |*e__| e__.deinit(); } const pem_key = switch (data.key) { @@ -203,12 +202,12 @@ fn setCredential( }; defer self.allocator.free(pem_key); - try e.setKeePassXCPasskeyValues( + e.setKeePassXCPasskeyValues( data.rp.id.get(), if (data.user.name) |name| name.get() else "", data.user.id.get(), pem_key, - ); + ) catch return TDatabase.Error.DatabaseError; // persist data save(self, self.allocator) catch { @@ -326,11 +325,11 @@ pub fn createDialog(allocator: std.mem.Allocator, path: []const u8) !std.fs.File }; defer db_key.deinit(); - var raw = std.ArrayList(u8).init(allocator); + var raw = std.Io.Writer.Allocating.init(allocator); defer raw.deinit(); database.save( - raw.writer(), + &raw.writer, db_key, allocator, ) catch |e| { @@ -338,7 +337,8 @@ pub fn createDialog(allocator: std.mem.Allocator, path: []const u8) !std.fs.File return error.DatabaseError; }; - f_db.writer().writeAll(raw.items) catch |e| { + var writer = f_db.writer(&.{}).interface; + writer.writeAll(raw.written()) catch |e| { std.log.err("Cannot write to database: {any}", .{e}); return error.DatabaseError; }; diff --git a/src/database/misc.zig b/src/database/misc.zig index 3df4b8d..9f74b8c 100644 --- a/src/database/misc.zig +++ b/src/database/misc.zig @@ -60,8 +60,9 @@ pub fn writeFile(path: []const u8, data: []const u8, a: std.mem.Allocator) !void return e; }; defer f2.close(); + var writer = f2.writer(&.{}); - f2.writer().writeAll(data) catch |e| { + writer.interface.writeAll(data) catch |e| { std.log.err("Cannot persist data: ({any})", .{e}); return e; }; diff --git a/src/main.zig b/src/main.zig index 02c121e..ed442d7 100644 --- a/src/main.zig +++ b/src/main.zig @@ -182,7 +182,13 @@ pub fn main() !void { } } } - std.time.sleep(10000000); + var timer = std.time.Timer.start() catch { + std.log.err("failed to start timer in main", .{}); + continue; + }; + while (timer.read() < 10000000) { + std.mem.doNotOptimizeAway(void); + } } } @@ -394,7 +400,7 @@ pub fn my_write( pub fn my_delete( id: [*c]const u8, -) callconv(.C) Error { +) callconv(.c) Error { _ = id; // TODO: remove this from keylib! diff --git a/src/state.zig b/src/state.zig index 469f385..a15fde2 100644 --- a/src/state.zig +++ b/src/state.zig @@ -1,5 +1,4 @@ const std = @import("std"); -const ccdb = @import("ccdb"); const Database = @import("Database.zig"); const Config = @import("database/Config.zig"); const keylib = @import("keylib"); @@ -63,10 +62,8 @@ pub fn authenticate(a: std.mem.Allocator) !void { const f = misc.openFile(conf.db_path) catch |e| blk: { if (e != error.WouldBlock) { if (std.mem.containsAtLeast(u8, conf.db_path, 1, ".ccdb")) { - break :blk Database.ccdb.createDialog(a, conf.db_path) catch |e_| { - std.log.err("unable to create database '{s}' ({any})", .{ conf.db_path, e }); - return e_; - }; + std.log.err("Databases of the format '.ccdb' are deprecated. Please check your config or use an earlier version of PassKeeZ.", .{}); + return error.DeprecatedDatabaseFormat; } else if (std.mem.containsAtLeast(u8, conf.db_path, 1, ".kdbx")) { break :blk Database.kdbx.createDialog(a, conf.db_path) catch |e_| { std.log.err("unable to create database '{s}' ({any})", .{ conf.db_path, e }); @@ -108,15 +105,30 @@ pub fn authenticate(a: std.mem.Allocator) !void { switch (password.term.Exited) { 0 => { - var db = if (std.mem.containsAtLeast(u8, conf.db_path, 1, ".ccdb")) blk: { - break :blk Database.ccdb.Database( - conf.db_path, - password.stdout[0 .. password.stdout.len - 1], - a, - ) catch { - std.log.err("unable to instantiate Database", .{}); - continue :outer; + var db = if (std.mem.containsAtLeast(u8, conf.db_path, 1, ".ccdb")) { + std.log.err("unsupported database {s}", .{conf.db_path}); + const r = std.process.Child.run(.{ + .allocator = a, + .argv = &.{ + "zigenity", + "--question", + "--window-icon=/usr/share/passkeez/passkeez.png", + "--icon=/usr/share/passkeez/passkeez-error.png", + "Databases of type .ccdb are deprecated and no longer supported.", + "Invalid database format", + "--ok-label=Ok", + "--switch-cancel", + "--timeout=15", + }, + }) catch |e2| { + std.log.err("unable to execute zigenity ({any})", .{e2}); + return e2; }; + defer { + a.free(r.stdout); + a.free(r.stderr); + } + return error.Failed; } else if (std.mem.containsAtLeast(u8, conf.db_path, 1, ".kdbx")) blk: { break :blk Database.kdbx.Database( conf.db_path, From 0f4791c8d6d583c6e86b1379a57c7653900e074f Mon Sep 17 00:00:00 2001 From: "David P. Sugar" Date: Mon, 29 Sep 2025 23:38:54 +0200 Subject: [PATCH 07/17] updated kdbx dependency with disabled Gzip support --- build.zig.zon | 5 ++--- src/database/kdbx.zig | 2 +- src/main.zig | 4 ++++ src/state.zig | 2 +- 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/build.zig.zon b/build.zig.zon index a1b71dc..272be06 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -13,9 +13,8 @@ .hash = "uuid-0.4.0-oOieIR2AAAChAUVBY4ABjYI1XN0EbVALmiN0JIlggC3i", }, .kdbx = .{ - //.url = "https://github.com/Zig-Sec/kdbx/archive/refs/tags/0.2.0.tar.gz", - //.hash = "kdbx-0.2.0-eJXZBNecEgC8JhA9_2_EfGy-4cvbEDQAITLXyrxdKACv", - .path = "../kdbx", + .url = "https://github.com/Zig-Sec/kdbx/archive/refs/tags/0.2.1.tar.gz", + .hash = "kdbx-0.2.0-eJXZBIihEgAl-5saSWpBoU8rVPLYgp_4SwVmMchYo-gC", }, }, .paths = .{ diff --git a/src/database/kdbx.zig b/src/database/kdbx.zig index 6300328..121bf06 100644 --- a/src/database/kdbx.zig +++ b/src/database/kdbx.zig @@ -266,7 +266,7 @@ pub fn createDialog(allocator: std.mem.Allocator, path: []const u8) !std.fs.File switch (r2.term.Exited) { 0 => { - std.log.info("{s}", .{r2.stdout}); + //std.debug.print("{s}", .{r2.stdout}); const pw1 = r2.stdout[0 .. r2.stdout.len - 1]; if (pw1.len < 8) { diff --git a/src/main.zig b/src/main.zig index ed442d7..2bb73bb 100644 --- a/src/main.zig +++ b/src/main.zig @@ -23,6 +23,10 @@ var fetch_rp: ?dt.ABS128T = null; var fetch_hash: ?[32]u8 = null; var fetch_ts: ?i64 = null; +pub const std_options: std.Options = .{ + .log_level = .warn, +}; + pub fn main() !void { State.init(allocator) catch |e| { std.log.err("Unable to initialize application ({any})", .{e}); diff --git a/src/state.zig b/src/state.zig index a15fde2..1caf266 100644 --- a/src/state.zig +++ b/src/state.zig @@ -101,7 +101,7 @@ pub fn authenticate(a: std.mem.Allocator) !void { @memset(password.stderr, 0); a.free(password.stderr); } - std.log.info("{any}", .{password}); + //std.debug.print("{any}", .{password}); switch (password.term.Exited) { 0 => { From d569f020aaec49dcb166b2ffc1bdb3a631f79cd6 Mon Sep 17 00:00:00 2001 From: "David P. Sugar" Date: Tue, 30 Sep 2025 12:23:20 +0200 Subject: [PATCH 08/17] updated kdbx dependency to 0.2.2 which includes a fix for custom icon support --- README.md | 2 +- build.zig.zon | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 644b342..1f1108d 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ To get started please visit the [wiki](https://github.com/Zig-Sec/PassKeeZ/wiki/ ### Database Management - KDBX: You can manage your `.kdbx` database with [KeePassXC](https://keepassxc.org/) or KeePass. -- CCDB: Currently the only way to manage your Credentials is by using the [CCDB command line application](https://github.com/r4gus/ccdb). + - _We recommend making regular backups of your KDBX database._ ### File synchronization diff --git a/build.zig.zon b/build.zig.zon index 272be06..556f7e1 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -13,8 +13,8 @@ .hash = "uuid-0.4.0-oOieIR2AAAChAUVBY4ABjYI1XN0EbVALmiN0JIlggC3i", }, .kdbx = .{ - .url = "https://github.com/Zig-Sec/kdbx/archive/refs/tags/0.2.1.tar.gz", - .hash = "kdbx-0.2.0-eJXZBIihEgAl-5saSWpBoU8rVPLYgp_4SwVmMchYo-gC", + .url = "https://github.com/Zig-Sec/kdbx/archive/refs/tags/0.2.2.tar.gz", + .hash = "kdbx-0.2.0-eJXZBEekEgBRiNkRMZ-B1Iv8ZyG687Y9z3KKkWAM2nZ1", }, }, .paths = .{ From b2df2103afa0abc4ebb1bda4b87aaa945a5d1074 Mon Sep 17 00:00:00 2001 From: "David P. Sugar" Date: Tue, 30 Sep 2025 21:34:23 +0200 Subject: [PATCH 09/17] updated install script to support newest release --- script/install-linux.sh | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/script/install-linux.sh b/script/install-linux.sh index 7b005de..db64f48 100755 --- a/script/install-linux.sh +++ b/script/install-linux.sh @@ -1,8 +1,8 @@ #!/bin/bash -PASSKEEZ_VERSION="0.5.3" -ZIGENITY_VERSION="0.5.0" -ZIG_VERSION="0.14.0" +PASSKEEZ_VERSION="0.6.0" +ZIGENITY_VERSION="0.6.0" +ZIG_VERSION="0.15.1" RED='\033[0;31m' GREEN='\033[0;32m' @@ -41,8 +41,14 @@ function get_package_manager { function download_zig { cd /tmp sub=$(ls | grep "zig-") - - curl -# -C - -o "zig.tar.xz" "https://ziglang.org/download/${ZIG_VERSION}/zig-linux-${ARCH}-${ZIG_VERSION}.tar.xz" + case "${ZIG_VERSION}" in + "0.14.0"|"0.14.1") + curl -# -C - -o "zig.tar.xz" "https://ziglang.org/download/${ZIG_VERSION}/zig-linux-${ARCH}-${ZIG_VERSION}.tar.xz" + ;; + *) + curl -# -C - -o "zig.tar.xz" "https://ziglang.org/download/${ZIG_VERSION}/zig-${ARCH}-linux-${ZIG_VERSION}.tar.xz" + ;; + esac tar -xf "zig.tar.xz" sub=$(ls | grep "zig-") @@ -127,8 +133,9 @@ function check_config_folder { } function postinst { - # Create a new group called fido - getent group fido || (groupadd fido && usermod -a -G fido $SUDO_USER) + # Create a new (system) group called fido + # - for explanation why a system group see: https://github.com/Zig-Sec/PassKeeZ/issues/21 + getent group fido || (groupadd -r fido && usermod -a -G fido $SUDO_USER) # Add uhid to the list of modules to load during boot echo "uhid" > /etc/modules-load.d/fido.conf From 7f0e6d6ed3679a5940d7f9a9dbc9cdea005e5926 Mon Sep 17 00:00:00 2001 From: "David P. Sugar" Date: Tue, 30 Sep 2025 21:45:45 +0200 Subject: [PATCH 10/17] replaced stupid timer loop (which was a place holder) with nanosleep --- src/main.zig | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/main.zig b/src/main.zig index 2bb73bb..6dabee7 100644 --- a/src/main.zig +++ b/src/main.zig @@ -186,13 +186,7 @@ pub fn main() !void { } } } - var timer = std.time.Timer.start() catch { - std.log.err("failed to start timer in main", .{}); - continue; - }; - while (timer.read() < 10000000) { - std.mem.doNotOptimizeAway(void); - } + std.posix.nanosleep(0, 10000000); } } From f773d382bdd62b2b53b0747e335b79d2aeb2154e Mon Sep 17 00:00:00 2001 From: "David P. Sugar" Date: Thu, 2 Oct 2025 16:17:18 +0200 Subject: [PATCH 11/17] fixes #23 --- build.zig.zon | 2 +- script/install-linux.sh | 2 +- src/database/Config.zig | 2 +- src/database/kdbx.zig | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/build.zig.zon b/build.zig.zon index 556f7e1..a2f4c97 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -1,6 +1,6 @@ .{ .name = .passkeez, - .version = "0.5.3", + .version = "0.6.1", .minimum_zig_version = "0.15.1", .fingerprint = 0xdd1692d15d21a6f6, .dependencies = .{ diff --git a/script/install-linux.sh b/script/install-linux.sh index db64f48..1407241 100755 --- a/script/install-linux.sh +++ b/script/install-linux.sh @@ -1,6 +1,6 @@ #!/bin/bash -PASSKEEZ_VERSION="0.6.0" +PASSKEEZ_VERSION="0.6.1" ZIGENITY_VERSION="0.6.0" ZIG_VERSION="0.15.1" diff --git a/src/database/Config.zig b/src/database/Config.zig index 07be539..9ff7e76 100644 --- a/src/database/Config.zig +++ b/src/database/Config.zig @@ -1,7 +1,7 @@ const std = @import("std"); const misc = @import("misc.zig"); -db_path: []const u8 = "~/.passkeez/db.ccdb", +db_path: []const u8 = "~/.passkeez/db.kdbx", lang: []const u8 = "english", pub fn load(a: std.mem.Allocator) !@This() { diff --git a/src/database/kdbx.zig b/src/database/kdbx.zig index 121bf06..50078f0 100644 --- a/src/database/kdbx.zig +++ b/src/database/kdbx.zig @@ -337,8 +337,8 @@ pub fn createDialog(allocator: std.mem.Allocator, path: []const u8) !std.fs.File return error.DatabaseError; }; - var writer = f_db.writer(&.{}).interface; - writer.writeAll(raw.written()) catch |e| { + var writer = f_db.writer(&.{}); + writer.interface.writeAll(raw.written()) catch |e| { std.log.err("Cannot write to database: {any}", .{e}); return error.DatabaseError; }; From b5280dbdf0dea60cdcf4183d05ea69483cfa2732 Mon Sep 17 00:00:00 2001 From: "David P. Sugar" Date: Tue, 7 Oct 2025 09:47:47 +0200 Subject: [PATCH 12/17] updated kdbx version to 0.2.3 --- build.zig.zon | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.zig.zon b/build.zig.zon index a2f4c97..bba6a71 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -13,8 +13,8 @@ .hash = "uuid-0.4.0-oOieIR2AAAChAUVBY4ABjYI1XN0EbVALmiN0JIlggC3i", }, .kdbx = .{ - .url = "https://github.com/Zig-Sec/kdbx/archive/refs/tags/0.2.2.tar.gz", - .hash = "kdbx-0.2.0-eJXZBEekEgBRiNkRMZ-B1Iv8ZyG687Y9z3KKkWAM2nZ1", + .url = "https://github.com/Zig-Sec/kdbx/archive/refs/tags/0.2.3.tar.gz", + .hash = "kdbx-0.2.3-eJXZBJWkEgDQRsIA_22SQSygPD6WlqEbjW83djSNqjQN", }, }, .paths = .{ From 96ccae1eeae38b2cb8bd86bffa7fee62806c4e14 Mon Sep 17 00:00:00 2001 From: "David P. Sugar" Date: Tue, 7 Oct 2025 09:48:44 +0200 Subject: [PATCH 13/17] bumped up own version --- build.zig.zon | 2 +- script/install-linux.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build.zig.zon b/build.zig.zon index bba6a71..2507f70 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -1,6 +1,6 @@ .{ .name = .passkeez, - .version = "0.6.1", + .version = "0.6.2", .minimum_zig_version = "0.15.1", .fingerprint = 0xdd1692d15d21a6f6, .dependencies = .{ diff --git a/script/install-linux.sh b/script/install-linux.sh index 1407241..5d4ca3c 100755 --- a/script/install-linux.sh +++ b/script/install-linux.sh @@ -1,6 +1,6 @@ #!/bin/bash -PASSKEEZ_VERSION="0.6.1" +PASSKEEZ_VERSION="0.6.2" ZIGENITY_VERSION="0.6.0" ZIG_VERSION="0.15.1" From 3fb9f3e036b01262e47a66624f20b0308b8c5b4a Mon Sep 17 00:00:00 2001 From: David Sugar Date: Sat, 6 Dec 2025 18:26:04 +0100 Subject: [PATCH 14/17] Enhance README with license badge and donation link Added license badge and donation link to README. --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 1f1108d..069a073 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,8 @@ ![Screenshot from 2025-04-15 11-16-56](https://github.com/user-attachments/assets/2ed6b32a-92d9-4fca-88ce-56f1e7b521ff) +![GitHub](https://img.shields.io/github/license/Zig-Sec/PassKeeZ?style=flat-square) + + PassKeeZ is a Passkey (FIDO2) compatible authenticator for Linux based on [keylib](https://github.com/r4gus/keylib). **To get started, please visit the [Wiki](https://github.com/Zig-Sec/PassKeeZ/wiki)**. From 0e1882ebbebd09e9a98311f929f9ead04a0dd0ab Mon Sep 17 00:00:00 2001 From: David Sugar Date: Sat, 13 Dec 2025 21:01:16 +0100 Subject: [PATCH 15/17] Enhance README with installation video link Added a video link for installation guidance. --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 069a073..de36499 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,9 @@ PassKeeZ is a Passkey (FIDO2) compatible authenticator for Linux based on [keylib](https://github.com/r4gus/keylib). -**To get started, please visit the [Wiki](https://github.com/Zig-Sec/PassKeeZ/wiki)**. +**To get started, please visit the [Wiki](https://github.com/Zig-Sec/PassKeeZ/wiki)**. The video below shows the full installation via the installer script. + +[![Installing PassKeeZ Video](https://img.youtube.com/vi/_Z2yj9kszvU/0.jpg)](https://www.youtube.com/watch?v=_Z2yj9kszvU) The project currently supports only Linux due to the absence of a standardized API for interprocess communication (IPC) between the client and authenticator. As a workaround, platform authenticators on Linux act as virtual USB HID devices utilizing uhid. However, extending this functionality to other platforms remains unexplored as I haven't had the opportunity to investigate the equivalent mechanisms elsewhere. From 90b1daa1b27ab7a8dc151a86007a7bf7c594b2ce Mon Sep 17 00:00:00 2001 From: j Date: Tue, 16 Dec 2025 00:40:32 +0100 Subject: [PATCH 16/17] fix icon path --- src/main.zig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main.zig b/src/main.zig index 6dabee7..7d2079e 100644 --- a/src/main.zig +++ b/src/main.zig @@ -301,8 +301,8 @@ pub fn my_up( .argv = &.{ "zigenity", "--question", - "--window-icon=/usr/local/bin/passkeez/passkeez.png", - "--icon=/usr/local/bin/passkeez/passkeez-question.png", + "--window-icon=/usr/share/passkeez/passkeez.png", + "--icon=/usr/share/passkeez/passkeez-question.png", text, i18n.get(State.conf.lang).user_presence_title, "--timeout=30", From 1854fb9e94b41f248af1bc3c63d148221dc4c222 Mon Sep 17 00:00:00 2001 From: David Sugar Date: Wed, 24 Dec 2025 18:28:15 +0100 Subject: [PATCH 17/17] Update README with Codeberg information Removed GitHub license badge and donation link, added note about Codeberg. --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index de36499..33a06d2 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,6 @@ ![Screenshot from 2025-04-15 11-16-56](https://github.com/user-attachments/assets/2ed6b32a-92d9-4fca-88ce-56f1e7b521ff) -![GitHub](https://img.shields.io/github/license/Zig-Sec/PassKeeZ?style=flat-square) - +## **Work is continued on Codeberg: https://codeberg.org/r4gus/PassKeeZ**. PassKeeZ is a Passkey (FIDO2) compatible authenticator for Linux based on [keylib](https://github.com/r4gus/keylib).