diff --git a/README.md b/README.md index 162de96..0aa9b58 100644 --- a/README.md +++ b/README.md @@ -20,17 +20,24 @@ Compilation / installation Dependencies: -* [libsodium](https://libsodium.org/) +* [libsodium](https://libsodium.org/) (*optional*) +* [zig](https://ziglang.org) -Compilation: +Compilation with libsodium: $ zig build -Drelease +Compilation without libsodium: + + $ zig build -Drelease -Dwithout_libsodium + The resulting binary can be found in `zig-out/bin/minisign`. ## Using cmake and gcc or clang: -* [libsodium](https://libsodium.org/) +Dependencies: + +* [libsodium](https://libsodium.org/) (*required*) * cmake * pkg-config * gcc or clang diff --git a/build.zig b/build.zig index f1e04fb..f0a655a 100644 --- a/build.zig +++ b/build.zig @@ -4,6 +4,8 @@ pub fn build(b: *std.Build) !void { const target = b.standardTargetOptions(.{}); const optimize = b.standardOptimizeOption(.{ .preferred_optimize_mode = .ReleaseSmall }); + const use_libzodium = b.option(bool, "without_libsodium", "Use the zig standard library instead of libsodium") orelse false; + const minisign = b.addExecutable(.{ .name = "minisign", .target = target, @@ -11,10 +13,27 @@ pub fn build(b: *std.Build) !void { .strip = true, }); minisign.linkLibC(); - minisign.root_module.linkSystemLibrary( - "sodium", - .{ .use_pkg_config = .yes }, - ); + if (use_libzodium) { + const libzodium_mod = b.createModule(.{ + .root_source_file = b.path("src/libzodium.zig"), + .target = target, + .optimize = optimize, + }); + const libzodium = b.addStaticLibrary(.{ + .name = "zodium", + .root_module = libzodium_mod, + .strip = true, + }); + libzodium.linkLibC(); + b.installArtifact(libzodium); + minisign.root_module.addCMacro("LIBZODIUM", "1"); + minisign.linkLibrary(libzodium); + } else { + minisign.root_module.linkSystemLibrary( + "sodium", + .{ .use_pkg_config = .yes }, + ); + } minisign.addIncludePath(b.path("src")); minisign.addSystemIncludePath(.{ .cwd_relative = "/opt/homebrew/include" }); minisign.addSystemIncludePath(.{ .cwd_relative = "/usr/local/include" }); diff --git a/src/helpers.c b/src/helpers.c index a151603..d8a04c6 100644 --- a/src/helpers.c +++ b/src/helpers.c @@ -1,10 +1,10 @@ #if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__)) || defined(__HAIKU__) -#include -#include -#include +# include +# include +# include #elif defined(_WIN32) -#include +# include #endif #include @@ -14,7 +14,11 @@ #include #include -#include +#ifdef LIBZODIUM +# include "zodium.h" +#else +# include +#endif #include "base64.h" #include "helpers.h" @@ -100,7 +104,7 @@ xor_buf(unsigned char *dst, const unsigned char *src, size_t len) int xfprintf(FILE *fp, const char *format, ...) { - char * out; + char *out; size_t out_maxlen = 4096U; int len; va_list va; @@ -126,7 +130,7 @@ int xfput_b64(FILE *fp, const unsigned char *bin, size_t bin_len) { const size_t b64_maxlen = (bin_len + 2) * 4 / 3 + 1; - char * b64; + char *b64; b64 = xsodium_malloc(b64_maxlen); if (bin_to_b64(b64, bin, b64_maxlen, bin_len) == NULL) { @@ -160,7 +164,7 @@ trim(char *str) while (i-- > (size_t) 0U) { if (str[i] == '\n') { str[i] = 0; - t = 1; + t = 1; } else if (str[i] == '\r') { str[i] = 0; } @@ -198,7 +202,7 @@ int basedir_create_useronly(const char *file) { const char *basename; - char * dir; + char *dir; int ret = -1; dir = xstrdup(file); diff --git a/src/libzodium.zig b/src/libzodium.zig new file mode 100644 index 0000000..c5c8ec9 --- /dev/null +++ b/src/libzodium.zig @@ -0,0 +1,120 @@ +const std = @import("std"); +const crypto = std.crypto; +const mem = std.mem; +const Ed25519 = crypto.sign.Ed25519; + +export fn sodium_init() callconv(.C) c_int { + return 0; +} + +export fn sodium_memzero(pnt: [*c]u8, len: usize) callconv(.C) void { + crypto.utils.secureZero(u8, pnt[0..len]); +} + +export fn randombytes_buf(pnt: [*c]u8, len: usize) callconv(.C) void { + crypto.random.bytes(pnt[0..len]); +} + +export fn sodium_malloc(len: usize) callconv(.C) ?*anyopaque { + return std.c.malloc(len); +} + +export fn sodium_free(pnt: ?*anyopaque) callconv(.C) void { + return std.c.free(pnt); +} + +export fn crypto_pwhash_scryptsalsa208sha256( + out: [*c]u8, + outlen: c_ulonglong, + passwd: [*c]const u8, + passwdlen: c_ulonglong, + salt: [*c]const u8, + opslimit: c_ulonglong, + memlimit: usize, +) callconv(.C) c_int { + crypto.pwhash.scrypt.kdf( + std.heap.c_allocator, + out[0..outlen], + passwd[0..passwdlen], + salt[0..32], + crypto.pwhash.scrypt.Params.fromLimits(opslimit, memlimit), + ) catch return -1; + return 0; +} + +const crypto_generichash_state = crypto.hash.blake2.Blake2b512; + +export fn crypto_generichash_init( + state: *crypto_generichash_state, + _: [*c]const u8, + _: usize, + outlen: usize, +) c_int { + state.* = crypto.hash.blake2.Blake2b512.init(.{ .expected_out_bits = outlen * 8 }); + return 0; +} + +export fn crypto_generichash_update( + state: *crypto_generichash_state, + in: [*c]const u8, + inlen: c_ulonglong, +) c_int { + state.*.update(in[0..inlen]); + return 0; +} + +export fn crypto_generichash_final( + state: *crypto_generichash_state, + out: [*c]u8, + outlen: usize, +) c_int { + var h: [64]u8 = undefined; + state.*.final(&h); + @memcpy(out[0..outlen], h[0..outlen]); + return 0; +} + +export fn crypto_sign_keypair(pk: [*c]u8, sk: [*c]u8) callconv(.C) c_int { + const kp = Ed25519.KeyPair.generate(); + pk[0..32].* = kp.public_key.toBytes(); + sk[0..64].* = kp.secret_key.toBytes(); + return 0; +} + +export fn crypto_sign_detached( + sig_bytes: [*c]u8, + _: [*c]c_ulonglong, + m: [*c]const u8, + mlen: c_ulonglong, + sk_bytes: [*c]const u8, +) callconv(.C) c_int { + const sk = Ed25519.SecretKey.fromBytes(sk_bytes[0..64].*) catch return -1; + const kp = Ed25519.KeyPair.fromSecretKey(sk) catch return -1; + var noise: [Ed25519.noise_length]u8 = undefined; + crypto.random.bytes(&noise); + const s = kp.sign(m[0..mlen], noise) catch return -1; + sig_bytes[0..64].* = s.toBytes(); + return 0; +} + +export fn crypto_sign_verify_detached( + sig_bytes: [*c]const u8, + m: [*c]const u8, + mlen: c_ulonglong, + pk_bytes: [*c]const u8, +) callconv(.C) c_int { + const pk = Ed25519.PublicKey.fromBytes(pk_bytes[0..32].*) catch return -1; + const sig = Ed25519.Signature.fromBytes(sig_bytes[0..64].*); + sig.verify(m[0..mlen], pk) catch return 1; + return 0; +} + +export fn sodium_bin2hex( + hex: [*c]u8, + hex_maxlen: usize, + bin: [*c]const u8, + bin_len: usize, +) callconv(.C) [*c]u8 { + _ = std.fmt.bufPrint(hex[0..hex_maxlen], "{s}", .{std.fmt.fmtSliceHexLower(bin[0..bin_len])}) catch return null; + return hex; +} diff --git a/src/minisign.c b/src/minisign.c index 588ab7c..f0e3a0c 100644 --- a/src/minisign.c +++ b/src/minisign.c @@ -10,7 +10,11 @@ #include #include -#include +#ifdef LIBZODIUM +# include "zodium.h" +#else +# include +#endif #include "base64.h" #include "get_line.h" @@ -829,28 +833,28 @@ main(int argc, char **argv) #ifndef VERIFY_ONLY char *sk_file = sig_default_skfile(); #endif - const char *sig_file = NULL; - const char *message_file = NULL; + const char *sig_file = NULL; + const char *message_file = NULL; #ifndef VERIFY_ONLY - const char *comment = NULL; + const char *comment = NULL; #endif - const char *pubkey_s = NULL; + const char *pubkey_s = NULL; #ifndef VERIFY_ONLY - const char *trusted_comment = NULL; + const char *trusted_comment = NULL; #endif - unsigned char opt_seen[16] = { 0 }; + unsigned char opt_seen[16] = { 0 }; int opt_flag; - int quiet = 0; - int output = 0; + int quiet = 0; + int output = 0; #ifndef VERIFY_ONLY - int force = 0; + int force = 0; #endif - int allow_legacy = 1; + int allow_legacy = 1; #ifndef VERIFY_ONLY - int sign_legacy = 0; - int unencrypted_key = 0; + int sign_legacy = 0; + int unencrypted_key = 0; #endif - Action action = ACTION_NONE; + Action action = ACTION_NONE; while ((opt_flag = getopt(argc, argv, getopt_options)) != -1) { switch (opt_flag) {