Compare commits

...

70 commits
0.10 ... master

Author SHA1 Message Date
Frank Denis
108ea640ba Require Zig 0.14 2025-03-11 19:06:46 +01:00
Frank Denis
c7d965e43a Update .gitignore 2025-03-11 19:04:58 +01:00
Frank Denis
428c18e08a Zig 0.14 compat 2025-03-11 19:04:40 +01:00
Frank Denis
32038530b7 Add a note about signify compatibility
Fixes #159
2025-03-11 18:43:41 +01:00
Chad Dougherty
e9631e8c67
Add a note in the man page about signify compatibility (#159)
based nearly verbatim on @jedisct1's explanation in the discussion of #59
2025-03-11 18:40:49 +01:00
Frank Denis
74365c0f51 Normalize Markdown files 2025-03-11 18:31:43 +01:00
Frank Denis
d5a2f02bc0 Look at possible include/lib paths for linuxbrew
Fixes #156
2025-01-29 23:05:34 +01:00
Frank Denis
4dd6fbf632 Use a list for possible include paths to be consistent with libraries 2025-01-29 23:02:34 +01:00
Frank Denis
41306e3e42 No need to set headerpad_max_install_names when linking statically 2025-01-29 18:28:41 +01:00
Frank Denis
090cc4752c Set headerpad_max_install_names only when linking libsodium 2025-01-29 18:27:40 +01:00
Rui Chen
b5cf334b42
build: fix mach-o relocation (#155)
Signed-off-by: Rui Chen <rui@chenrui.dev>
2025-01-29 18:27:25 +01:00
Frank Denis
c684406e21 Don't include nonexistent library paths
Introduced in 101e90a668 to make
Minisign build with Zig 0.13.

Fixes #153
2025-01-18 14:46:57 +01:00
Frank Denis
29a07eade0 Add compatibility with 32-bit targets (webassembly) 2025-01-16 13:36:56 +01:00
Frank Denis
90d46db240 Add the ability to override the libsodium install dirs 2025-01-16 12:55:52 +01:00
Frank Denis
101e90a668 Allow compilation with Zig 0.13 as well as 0.14
Fixes #150
2025-01-16 00:54:22 +01:00
Frank Denis
c165362385 Move libzodium files to a dedicated directory 2025-01-15 23:42:24 +01:00
Frank Denis
1c44ef601e Regen man page 2025-01-15 23:42:04 +01:00
Frank Denis
3c889ce7f3 Add a section title 2025-01-15 23:27:20 +01:00
Frank Denis
b85e15d45a Add build.zig.zon 2025-01-15 22:46:54 +01:00
Frank Denis
9896d072d4 Bump 2025-01-15 22:46:54 +01:00
Frank Denis
b393ff47b1 Let people compile with ReleaseFast if they want to 2025-01-15 22:46:54 +01:00
Frank Denis
c084f9ca38 without_libsodium -> without-libsodium 2025-01-15 22:46:54 +01:00
Frank Denis
c1c452560f Nits 2025-01-15 22:46:51 +01:00
Frank Denis
e0e86b4e4f Add -Dstatic 2025-01-15 22:46:51 +01:00
Frank Denis
12333fd7d1 Minisign is 10 years old 2025-01-15 22:46:51 +01:00
Frank Denis
952225f9b7 Import zodium.h 2025-01-15 22:46:51 +01:00
Frank Denis
e43b9ff13b Make libsodium optional 2025-01-15 22:11:52 +01:00
Frank Denis
e640be4b8f Indent 2025-01-15 22:07:37 +01:00
Frank Denis
a1c07cc277 Leverage root_module.linkSystemLibrary() 2025-01-15 21:03:56 +01:00
Frank Denis
285b53d52c Mention where the compiled binary is 2025-01-15 21:03:49 +01:00
Frank Denis
28612a431a Add issues.yml 2024-12-31 14:58:15 +01:00
Frank Denis
cbc79b3ece defineCMacro -> root_module.addCMacro 2024-12-20 12:21:03 +01:00
Frank Denis
55320cc84c 2024 2024-12-11 18:46:14 +01:00
Frank Denis
868785690f Add a Docker usage example
Fixes #132
2024-12-11 18:45:20 +01:00
Frank Denis
45478e1dd6 Udpate for Zig 0.13 2024-05-09 21:46:20 +02:00
Ryan Castellucci
996ea4fee3
exclude other unneeded code for VERIFY_ONLY (#139) 2024-04-02 08:11:27 +02:00
Frank Denis
573988d235 Update for zig-master 2024-01-04 12:24:31 +01:00
Isaac Yonemoto
5f96819f44
Add Elixir to minisign implementations (#138) 2023-12-18 18:50:08 +01:00
Frank Denis
ef2db96ae0 Breaking: wero-pad key identifiers
Fixes #137
2023-12-17 22:04:43 +01:00
Frank Denis
7d1116c5cc Zig 0.12 compat 2023-11-20 19:13:22 +01:00
Frank Denis
8aef018182 Update for Zig 0.11 2023-08-12 11:21:37 +02:00
Frank Denis
709fed6b73 Build script 2023-01-17 11:48:15 +01:00
Frank Denis
05a0cd435d Remove Travis 2023-01-17 11:40:20 +01:00
Frank Denis
444ef2dca9 Remove unused var 2023-01-16 22:42:27 +01:00
Frank Denis
ee46615226 Zeroize seckey_struct early 2023-01-16 21:24:08 +01:00
Frank Denis
12fd90b6fe Update man page 2023-01-16 21:18:39 +01:00
Frank Denis
ef445ab928 2023 2023-01-16 21:16:52 +01:00
Frank Denis
3f1df52db4 Warn about -C -W implications before it's too late 2023-01-16 21:16:38 +01:00
Frank Denis
506ac9ce3e Simplify usage without key encryption 2023-01-16 21:12:16 +01:00
Frank Denis
27bff8a1f5 Use CMAKE_BUILD_TYPE=MinSizeRel 2023-01-16 20:50:57 +01:00
Frank Denis
80defb2a99 Bump 2023-01-16 20:46:34 +01:00
Frank Denis
6040047c27 seckey_chk() actually computes chk 2023-01-16 20:46:34 +01:00
Frank Denis
16624abf2e Add the ability to change the password of a secret key
Fixes #110
2023-01-16 20:39:56 +01:00
Frank Denis
6ccd801dbc Allow seckey_load() to optionally return a copy of the key comment 2023-01-16 20:04:16 +01:00
Frank Denis
41938e99f5 Move key encryption/decryption code to dedicated functions 2023-01-16 19:45:00 +01:00
Frank Denis
a536c178ab Format 2023-01-16 19:40:34 +01:00
Frank Denis
94e18a72d8 Move variables to a local context 2023-01-16 19:38:39 +01:00
Frank Denis
d870938ae5 Reorder commands for clarity 2023-01-16 19:21:19 +01:00
Frank Denis
d2afa89fe7 Update usage 2023-01-16 19:15:53 +01:00
Frank Denis
c33051dec3 Update usage 2023-01-16 19:03:20 +01:00
Frank Denis
cb2fc2ce7c Add a flag to allow secret keys to be unencrypted
https://github.com/ziglang/zig/issues/4945
2023-01-16 18:50:36 +01:00
Frank Denis
0fccaf94e2 Require cmake 2.8.12 2023-01-16 18:43:18 +01:00
Frank Denis
a26f2c8e5d Update build.zig for zig 0.10 2023-01-11 01:27:48 +01:00
Frank Denis
a2c8848418 Add cosign public key
Fixes #121
2022-06-10 14:17:52 +02:00
Frank Denis
4b2df2ee07 Bail out if fgets() output is not \n terminated when it should
Fixes #116
2022-02-20 10:50:37 +01:00
Frank Denis
b81f3d4065 Add paths for Homebrew on Apple Silicon 2021-11-30 09:22:24 +01:00
Frank Denis
e74428c464 Update man page
Fixes #106
2021-10-25 00:53:09 +02:00
Frank Denis
aa99a5c417 Document -f option 2021-10-09 17:47:16 +02:00
Frank Denis
614fd57665 Define _GNU_SOURCE 2021-10-09 15:54:09 +02:00
Frank Denis
503f722ac3 Suggest release-safe 2021-10-09 15:34:41 +02:00
22 changed files with 742 additions and 411 deletions

View file

@ -94,7 +94,7 @@ IncludeIsMainSourceRegex: ""
IndentCaseLabels: false
IndentCaseBlocks: false
IndentGotoLabels: true
IndentPPDirectives: None
IndentPPDirectives: AfterHash
IndentExternBlock: AfterExternBlock
IndentRequires: false
IndentWidth: 4

17
.github/workflows/issues.yml vendored Normal file
View file

@ -0,0 +1,17 @@
name: Close inactive issues
on:
schedule:
- cron: "30 1 * * *"
jobs:
close-issues:
runs-on: ubuntu-latest
permissions:
issues: write
pull-requests: write
steps:
- uses: actions/stale@v9
with:
stale-issue-message: "This issue is stale because it has been open for 30 days with no activity."
close-issue-message: "This issue was closed because it has been inactive for 14 days since being marked as stale."
repo-token: ${{ secrets.GITHUB_TOKEN }}

2
.gitignore vendored
View file

@ -23,5 +23,5 @@ CMakeFiles
Makefile
cmake_install.cmake
minisign
zig-cache
.zig-cache
zig-out

View file

@ -1,43 +0,0 @@
sudo: required
language: c
os:
- linux
compiler:
- gcc
before_script:
- git clone https://github.com/jedisct1/libsodium.git --branch=stable
- cd libsodium
- env CPPFLAGS=-DED25519_NONDETERMINISTIC ./configure --disable-dependency-tracking
- make -j$(nproc)
- sudo make install
- sudo ldconfig
- cd ..
script:
- rm -fr build
- mkdir build
- cd build
- cmake ..
- make -j$(nproc)
- cd ..
- rm -fr build
- mkdir build
- cd build
- cmake -D STATIC_LIBSODIUM=1 ..
- make -j$(nproc)
- cd ..
- rm -fr build
- mkdir build
- cd build
- cmake -D BUILD_STATIC_EXECUTABLES=1 ..
- make -j$(nproc)
- cd ..
matrix:
- fast_finish: true

View file

@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 2.8)
cmake_minimum_required(VERSION 3.10)
project(minisign C)
@ -7,7 +7,7 @@ set(CPACK_PACKAGE_VENDOR "Frank Denis")
set(CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_CURRENT_SOURCE_DIR}/README.md")
set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE")
set(CPACK_PACKAGE_VERSION_MAJOR "0")
set(CPACK_PACKAGE_VERSION_MINOR "10")
set(CPACK_PACKAGE_VERSION_MINOR "12")
set(CPACK_PACKAGE_VERSION_PATCH "0")
set(
CPACK_SOURCE_PACKAGE_FILE_NAME

View file

@ -7,7 +7,7 @@ RUN apk add --no-cache upx ||:
RUN curl https://download.libsodium.org/libsodium/releases/LATEST.tar.gz | tar xzvf - && cd libsodium-stable && env CFLAGS="-Os" CPPFLAGS="-DED25519_NONDETERMINISTIC=1" ./configure --disable-dependency-tracking && make -j$(nproc) check && make install && cd .. && rm -fr libsodium-stable
COPY ./ ./
RUN mkdir build && cd build && cmake -D BUILD_STATIC_EXECUTABLES=1 .. && make -j$(nproc)
RUN mkdir build && cd build && cmake -DCMAKE_BUILD_TYPE=MinSizeRel -DBUILD_STATIC_EXECUTABLES=1 .. && make -j$(nproc)
RUN upx --lzma build/minisign ||:
FROM scratch

View file

@ -1,7 +1,7 @@
ISC LICENSE.
/*
* Copyright (c) 2015-2021
* Copyright (c) 2015-2025
* Frank Denis <j at pureftpd dot org>
*
* Permission to use, copy, modify, and/or distribute this software for any

115
README.md
View file

@ -1,7 +1,6 @@
![CodeQL scan](https://github.com/jedisct1/minisign/workflows/CodeQL%20scan/badge.svg)
Minisign
========
# Minisign
Minisign is a dead simple tool to sign files and verify signatures.
@ -13,25 +12,39 @@ public key:
RWQf6LRCGA9i53mlYecO4IzT51TGPpvWucNSCh1CBM0QTaLn73Y7GFO3
Compilation / installation
--------------------------
## Compilation / installation
## Using [Zig](https://ziglang.org):
## Building with Zig
Dependencies:
* [libsodium](https://libsodium.org/)
- [libsodium](https://libsodium.org/) (_optional_)
- [zig](https://ziglang.org)
Compilation:
Compilation with libsodium, dynamically linked (libsodium will need to be installed on the system for the command to run):
$ zig build -Drelease-small
$ zig build -Doptimize=ReleaseSmall
## Using cmake and gcc or clang:
Compilation with libsodium, statically linked (libsodium will only be needed for compilation):
* [libsodium](https://libsodium.org/)
* cmake
* pkg-config
* gcc or clang
$ zig build -Doptimize=ReleaseSmall -Dstatic
Compilation without libsodium, no dependencies required:
$ zig build -Doptimize=ReleaseSmall -Dwithout-libsodium
The resulting binary can be found in `zig-out/bin/minisign`.
In all these examples, `ReleaseFast` can be replaced with `ReleaseSmall` to favor speed over size.
## Building with cmake and gcc or clang:
Dependencies:
- [libsodium](https://libsodium.org/) (_required_)
- cmake
- pkg-config
- gcc or clang
Compilation:
@ -49,6 +62,8 @@ or:
$ cmake -D BUILD_STATIC_EXECUTABLES=1 ..
## Pre-built packages
Minisign is also available in Homebrew:
$ brew install minisign
@ -65,37 +80,53 @@ Minisign is also available with docker:
$ docker run -i --rm jedisct1/minisign
Additional tools, libraries and implementations
-----------------------------------------------
For example, verifying a signature using the docker image can be done
with:
* [minizign](https://github.com/jedisct1/zig-minisign) is a compact
implementation in Zig, that can also use ssh-encoded keys.
* [minisign-misc](https://github.com/JayBrown/minisign-misc) is a very
nice set of workflows and scripts for macOS to verify and sign files
with minisign.
* [go-minisign](https://github.com/jedisct1/go-minisign) is a small module
in Go to verify Minisign signatures.
* [rust-minisign](https://github.com/jedisct1/rust-minisign) is a Minisign
library written in pure Rust, that can be embedded in other applications.
* [rsign2](https://github.com/jedisct1/rsign2) is a reimplementation of
the command-line tool in Rust.
* [minisign (go)](https://github.com/aead/minisign) is a rewrite of Minisign
in the Go language. It reimplements the CLI but can also be used as a library.
* [minisign-verify](https://github.com/jedisct1/rust-minisign-verify) is
a small Rust crate to verify Minisign signatures.
* [minisign-net](https://github.com/bitbeans/minisign-net) is a .NET library
to handle and create Minisign signatures.
* [minisign](https://github.com/chm-diederichs/minisign) a Javascript
implementation.
* WebAssembly implementations of [rsign2](https://wapm.io/package/jedisct1/rsign2)
and [minisign-cli](https://wapm.io/package/jedisct1/minisign) are available on
WAPM.
* [minisign-php](https://github.com/soatok/minisign-php) is a PHP implementation.
* [py-minisign](https://github.com/x13a/py-minisign) is a Python
implementation.
$ docker run -v .:/minisign -e HOME=/minisign -w /minisign \
-it --rm jedisct1/minisign \
-Vm file_to_verify -p minisign.pub
Signature determinism
---------------------
The image can be verified with the following cosign public key:
```text
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAExjZWrlc6c58W7ZzmQnx6mugty99C
OQTDtJeciX9LF9hEbs1J1fzZHRdRhV4OTqcq0jTW9PXnrSSZlk1fbkE/5w==
-----END PUBLIC KEY-----
```
## Additional tools, libraries and implementations
- [minizign](https://github.com/jedisct1/zig-minisign) is a compact
implementation in Zig, that can also use ssh-encoded keys.
- [minisign-misc](https://github.com/JayBrown/minisign-misc) is a very
nice set of workflows and scripts for macOS to verify and sign files
with minisign.
- [go-minisign](https://github.com/jedisct1/go-minisign) is a small module
in Go to verify Minisign signatures.
- [rust-minisign](https://github.com/jedisct1/rust-minisign) is a Minisign
library written in pure Rust, that can be embedded in other applications.
- [rsign2](https://github.com/jedisct1/rsign2) is a reimplementation of
the command-line tool in Rust.
- [minisign (go)](https://github.com/aead/minisign) is a rewrite of Minisign
in the Go language. It reimplements the CLI but can also be used as a library.
- [minisign-verify](https://github.com/jedisct1/rust-minisign-verify) is
a small Rust crate to verify Minisign signatures.
- [minisign-net](https://github.com/bitbeans/minisign-net) is a .NET library
to handle and create Minisign signatures.
- [minisign](https://github.com/chm-diederichs/minisign) a Javascript
implementation.
- WebAssembly implementations of [rsign2](https://wapm.io/package/jedisct1/rsign2)
and [minisign-cli](https://wapm.io/package/jedisct1/minisign) are available on
WAPM.
- [minisign-php](https://github.com/soatok/minisign-php) is a PHP implementation.
- [py-minisign](https://github.com/x13a/py-minisign) is a Python
implementation.
- [minisign](https://hexdocs.pm/minisign/Minisign.html) is an Elixir implementation
(verification only)
## Signature determinism
This implementation uses deterministic signatures, unless libsodium
was compiled with the `ED25519_NONDETERMINISTIC` macro defined. This

4
build-dist-package.sh Executable file
View file

@ -0,0 +1,4 @@
#! /bin/sh
tar czpvf minisign-0.12.tar.gz $(git ls-files)
minisign -Sm minisign-0.12.tar.gz

View file

@ -1,17 +1,80 @@
const builtin = @import("builtin");
const std = @import("std");
pub fn build(b: *std.build.Builder) !void {
var target = b.standardTargetOptions(.{});
const mode = b.standardReleaseOptions();
pub fn build(b: *std.Build) !void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const use_libzodium = b.option(bool, "without-libsodium", "Use the zig standard library instead of libsodium") orelse false;
const use_static_linking = b.option(bool, "static", "Statically link the binary") orelse false;
const minisign = b.addExecutable(.{
.name = "minisign",
.target = target,
.optimize = optimize,
.strip = true,
});
if (builtin.zig_version.major == 0 and builtin.zig_version.minor < 14) {
@compileError("Building requires Zig 0.14.0 or later");
}
// fix Mach-O relocation
minisign.headerpad_max_install_names = true;
const minisign = b.addExecutable("minisign", null);
minisign.setTarget(target);
minisign.setBuildMode(mode);
minisign.install();
minisign.linkLibC();
minisign.linkSystemLibrary("sodium");
if (use_libzodium) {
var libzodium = lib: {
const libzodium_mod = b.createModule(.{
.root_source_file = b.path("src/libzodium/libzodium.zig"),
.target = target,
.optimize = optimize,
});
break :lib 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 {
var override_pkgconfig = false;
if (std.posix.getenv("LIBSODIUM_INCLUDE_PATH")) |path| {
minisign.addSystemIncludePath(.{ .cwd_relative = path });
override_pkgconfig = true;
}
if (std.posix.getenv("LIBSODIUM_LIB_PATH")) |path| {
minisign.addLibraryPath(.{ .cwd_relative = path });
override_pkgconfig = true;
}
minisign.addIncludeDir("src");
minisign.addSystemIncludeDir("/usr/local/include");
minisign.addCSourceFiles(&.{ "src/base64.c", "src/get_line.c", "src/helpers.c", "src/minisign.c" }, &.{});
for ([_][]const u8{ "/opt/homebrew/include", "/home/linuxbrew/.linuxbrew/include", "/usr/local/include" }) |path| {
std.fs.accessAbsolute(path, .{}) catch continue;
minisign.addSystemIncludePath(.{ .cwd_relative = path });
}
for ([_][]const u8{ "/opt/homebrew/lib", "/home/linuxbrew/.linuxbrew/lib", "/usr/local/lib" }) |path| {
std.fs.accessAbsolute(path, .{}) catch continue;
minisign.addLibraryPath(.{ .cwd_relative = path });
}
if (!use_static_linking) {
minisign.headerpad_max_install_names = true; // required to compile using Homebrew, see https://github.com/jedisct1/minisign/pull/155
}
minisign.root_module.linkSystemLibrary(
"sodium",
.{
.use_pkg_config = if (override_pkgconfig) .no else .yes,
.preferred_link_mode = if (use_static_linking) .static else .dynamic,
},
);
}
minisign.addIncludePath(b.path("src"));
minisign.root_module.addCMacro("_GNU_SOURCE", "1");
const source_files = &.{ "src/base64.c", "src/get_line.c", "src/helpers.c", "src/minisign.c" };
minisign.addCSourceFiles(.{ .files = source_files });
b.installArtifact(minisign);
}

13
build.zig.zon Normal file
View file

@ -0,0 +1,13 @@
.{
.name = .minisign,
.version = "0.12.0",
.fingerprint = 0x280456c1fd373c55,
.paths = .{
"LICEMSE",
"README.md",
"build.zig",
"build.zig.zon",
"src",
"share",
},
}

4
cosign.pub Normal file
View file

@ -0,0 +1,4 @@
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAExjZWrlc6c58W7ZzmQnx6mugty99C
OQTDtJeciX9LF9hEbs1J1fzZHRdRhV4OTqcq0jTW9PXnrSSZlk1fbkE/5w==
-----END PUBLIC KEY-----

View file

@ -1,22 +1,25 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
.TH "MINISIGN" "1" "June 2020" "" ""
.TH "MINISIGN" "1" "March 2025" "" ""
.
.SH "NAME"
\fBminisign\fR \- A dead simple tool to sign files and verify signatures\.
.
.SH "SYNOPSIS"
\fBminisign\fR \-G [\-p pubkey] [\-s seckey]
\fBminisign\fR \-G [\-p pubkey_file] [\-s seckey_file] [\-W]
.
.P
\fBminisign\fR \-S [\-H] [\-x sigfile] [\-s seckey] [\-c untrusted_comment] [\-t trusted_comment] \-m file [file \.\.\.]
\fBminisign\fR \-R [\-s seckey_file] [\-p pubkey_file]
.
.P
\fBminisign\fR \-V [\-x sigfile] [\-p pubkeyfile | \-P pubkey] [\-o] [\-q] \-m file
\fBminisign\fR \-C [\-s seckey_file] [\-W]
.
.P
\fBminisign\fR \-R \-s seckey \-p pubkeyfile
\fBminisign\fR \-S [\-H] [\-x sig_file] [\-s seckey_file] [\-c untrusted_comment] [\-t trusted_comment] \-m file [file \.\.\.]
.
.P
\fBminisign\fR \-V [\-x sig_file] [\-p pubkey_file | \-P pubkey] [\-o] [\-q] \-m file
.
.SH "DESCRIPTION"
\fBMinisign\fR is a dead simple tool to sign files and verify signatures\.
@ -32,6 +35,14 @@ These options control the actions of \fBminisign\fR\.
Generate a new key pair
.
.TP
\fB\-C\fR
Change/remove the password of a secret key
.
.TP
\fB\-R\fR
Recreate a public key file from a secret key file
.
.TP
\fB\-S\fR
Sign files
.
@ -40,6 +51,14 @@ Sign files
Verify that a signature is valid for a given file
.
.TP
\fB\-H\fR
Requires the input to be prehashed
.
.TP
\fB\-l\fR
Sign using the legacy format
.
.TP
\fB\-m <file>\fR
File to sign/verify
.
@ -48,11 +67,7 @@ File to sign/verify
Combined with \-V, output the file content after verification
.
.TP
\fB\-H\fR
Combined with \-S, pre\-hash in order to sign large files
.
.TP
\fB\-p <pubkeyfile>\fR
\fB\-p <pubkey_file>\fR
Public key file (default: \./minisign\.pub)
.
.TP
@ -60,11 +75,15 @@ Public key file (default: \./minisign\.pub)
Public key, as a base64 string
.
.TP
\fB\-s <seckey>\fR
\fB\-s <seckey_file>\fR
Secret key file (default: ~/\.minisign/minisign\.key)
.
.TP
\fB\-x <sigfile>\fR
\fB\-W\fR
Do not encrypt/decrypt the secret key with a password
.
.TP
\fB\-x <sig_file>\fR
Signature file (default: <file>\.minisig)
.
.TP
@ -84,10 +103,6 @@ Quiet mode, suppress output
Pretty quiet mode, only print the trusted comment
.
.TP
\fB\-R\fR
Recreate a public key file from a secret key file
.
.TP
\fB\-f\fR
Force\. Combined with \-G, overwrite a previous key pair
.
@ -137,53 +152,17 @@ This requires the signature \fBmyfile\.txt\.minisig\fR to be present in the same
.P
The public key can either reside in a file (\fB\./minisign\.pub\fR by default) or be directly specified on the command line\.
.
.SH "Notes"
\fBTrusted comments\fR
.SH "NOTES"
Signature files include an untrusted comment line that can be freely modified even after the signature is created\.
.
.P
Signature files include an untrusted comment line that can be freely modified, even after signature creation\.
They also include a second comment line that cannot be modified without the secret key\.
.
.P
They also include a second comment line, that cannot be modified without the secret key\.
Trusted comments can be used to add instructions or application\-specific metadata such as the intended file name, timestamps, resource identifiers, or version numbers to prevent downgrade attacks\.
.
.P
Trusted comments can be used to add instructions or application\-specific metadata (intended file name, timestamps, resource identifiers, version numbers to prevent downgrade attacks)\.
.
.P
\fBCompatibility with OpenBSD signify\fR
.
.P
Signatures written by \fBminisign\fR can be verified using OpenBSD\'s \fBsignify\fR tool: public key files and signature files are compatible\.
.
.P
However, \fBminisign\fR uses a slightly different format to store secret keys\.
.
.P
\fBMinisign\fR signatures include trusted comments in addition to untrusted comments\. Trusted comments are signed, thus verified, before being displayed\.
.
.P
This adds two lines to the signature files, that signify silently ignores\.
.
.P
\fBPre\-hashing\fR
.
.P
By default, signing and verification require as much memory as the size of the file\.
.
.P
Since \fBMinisign 0\.6\fR, huge files can be signed and verified with very low memory requirements, by pre\-hashing the content\.
.
.P
The \-H command\-line switch, in combination with \-S, generates a pre\-hashed signature (HashEdDSA):
.
.P
$ \fBminisign\fR \-SHm myfile\.txt
.
.P
Verification of such a signature doesn\'t require any specific switch: the appropriate algorithm will automatically be detected\.
.
.P
Signatures generated that way are not compatible with OpenBSD\'s \fBsignify\fR tool and are not compatible with \fBMinisign\fR versions prior to 0\.6\.
OpenBSD\'s \fBsignify(1)\fR is conceptually similar to Minisign\. Minisign creates signatures that can be verified by \fBsignify\fR; however, signatures created by \fBsignify\fR cannot be verified with Minisign because Minisign expects a trusted comment section to be present\. Trusted comments are crucial for describing what has been signed, in addition to merely confirming that a signature exists\.
.
.SH "AUTHOR"
Frank Denis (github [at] pureftpd [dot] org)

View file

@ -52,7 +52,7 @@ b64_to_bin(unsigned char *const bin, const char *b64, size_t bin_maxlen, size_t
REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE
};
const unsigned char *b64_u = (const unsigned char *) b64;
unsigned char * bin_w = bin;
unsigned char *bin_w = bin;
unsigned char mask = 0U;
unsigned char t0 = 0, t1 = 0, t2 = 0, t3 = 0;
uint32_t t = 0;

View file

@ -1,6 +1,6 @@
#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__)) || defined(__HAIKU__)
#include <sys/types.h>
# include <sys/types.h>
#endif
#include <assert.h>
@ -10,19 +10,19 @@
#include <string.h>
#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__)) || defined(__HAIKU__)
#include <fcntl.h>
#include <poll.h>
#include <termios.h>
#include <unistd.h>
# include <fcntl.h>
# include <poll.h>
# include <termios.h>
# include <unistd.h>
#elif defined(_WIN32)
#include <windows.h>
# include <windows.h>
#endif
#include "get_line.h"
#include "helpers.h"
#ifndef TCSAFLUSH
#define TCSAFLUSH 0
# define TCSAFLUSH 0
#endif
#ifndef VERIFY_ONLY
@ -33,7 +33,7 @@ disable_echo(void)
fflush(stdout);
fflush(stderr);
#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__)) || defined(__HAIKU__)
# if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__)) || defined(__HAIKU__)
{
struct termios p;
@ -43,7 +43,7 @@ disable_echo(void)
p.c_lflag &= ~ECHO;
tcsetattr(0, TCSAFLUSH, &p);
}
#elif defined(_WIN32)
# elif defined(_WIN32)
{
HANDLE handle = GetStdHandle(STD_INPUT_HANDLE);
DWORD mode = 0;
@ -51,7 +51,7 @@ disable_echo(void)
GetConsoleMode(handle, &mode);
SetConsoleMode(handle, mode & ~ENABLE_ECHO_INPUT);
}
#endif
# endif
}
static void
@ -60,7 +60,7 @@ enable_echo(void)
fflush(stdout);
fflush(stderr);
#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__)) || defined(__HAIKU__)
# if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__)) || defined(__HAIKU__)
{
struct termios p;
@ -70,7 +70,7 @@ enable_echo(void)
p.c_lflag |= ECHO;
tcsetattr(0, TCSAFLUSH, &p);
}
#elif defined(_WIN32)
# elif defined(_WIN32)
{
HANDLE handle = GetStdHandle(STD_INPUT_HANDLE);
DWORD mode = 0;
@ -78,7 +78,7 @@ enable_echo(void)
GetConsoleMode(handle, &mode);
SetConsoleMode(handle, mode | ENABLE_ECHO_INPUT);
}
#endif
# endif
}
int

View file

@ -1,10 +1,10 @@
#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__)) || defined(__HAIKU__)
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
# include <fcntl.h>
# include <sys/stat.h>
# include <sys/types.h>
#elif defined(_WIN32)
#include <direct.h>
# include <direct.h>
#endif
#include <errno.h>
@ -14,7 +14,11 @@
#include <stdlib.h>
#include <string.h>
#include <sodium.h>
#ifdef LIBZODIUM
# include "libzodium/sodium.h"
#else
# include <sodium.h>
#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) {
@ -151,16 +155,21 @@ xfclose(FILE *fp)
return 0;
}
void
int
trim(char *str)
{
size_t i = strlen(str);
int t = 0;
while (i-- > (size_t) 0U) {
if (str[i] == '\n' || str[i] == '\r') {
if (str[i] == '\n') {
str[i] = 0;
t = 1;
} else if (str[i] == '\r') {
str[i] = 0;
}
}
return t;
}
const char *
@ -193,7 +202,7 @@ int
basedir_create_useronly(const char *file)
{
const char *basename;
char * dir;
char *dir;
int ret = -1;
dir = xstrdup(file);

View file

@ -6,12 +6,12 @@
#include <stdio.h>
#if !defined(__GNUC__) && !defined(__attribute__)
#define __attribute__(X)
# define __attribute__(X)
#endif
#ifdef _WIN32
#define DIR_SEP '\\'
# define DIR_SEP '\\'
#else
#define DIR_SEP '/'
# define DIR_SEP '/'
#endif
uint64_t le64_load(const unsigned char *p);
@ -36,7 +36,7 @@ int xfprintf(FILE *fp, const char *format, ...) __attribute__((format(printf, 2,
int xfclose(FILE *fp);
void trim(char *str);
int trim(char *str);
const char *file_basename(const char *file);

120
src/libzodium/libzodium.zig Normal file
View file

@ -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..@intCast(outlen)],
passwd[0..@intCast(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..@intCast(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 = if (std.meta.hasFn(Ed25519.KeyPair, "generate")) Ed25519.KeyPair.generate() else (Ed25519.KeyPair.create(null) catch return -1);
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..@intCast(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..@intCast(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;
}

62
src/libzodium/sodium.h Normal file
View file

@ -0,0 +1,62 @@
#pragma once
#include <stddef.h>
int sodium_init(void) __attribute__((warn_unused_result));
;
void sodium_memzero(void* const pnt, const size_t len);
void randombytes_buf(void* const buf, const size_t size) __attribute__((nonnull));
void* sodium_malloc(const size_t size) __attribute__((malloc));
void sodium_free(void* ptr);
#define crypto_pwhash_scryptsalsa208sha256_SALTBYTES 32U
#define crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN 32768U
#define crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN 16777216U
#define crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_SENSITIVE 33554432U
#define crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_SENSITIVE 1073741824U
int crypto_pwhash_scryptsalsa208sha256(unsigned char* const out,
unsigned long long outlen,
const char* const passwd,
unsigned long long passwdlen,
const unsigned char* const salt,
unsigned long long opslimit,
size_t memlimit) __attribute__((warn_unused_result))
__attribute__((nonnull));
typedef struct crypto_generichash_state {
unsigned char opaque[512];
} crypto_generichash_state;
#define crypto_generichash_BYTES_MAX 64U
#define crypto_generichash_BYTES 32U
int crypto_generichash_init(crypto_generichash_state* state, const unsigned char* key,
const size_t keylen, const size_t outlen) __attribute__((nonnull(1)));
int crypto_generichash_update(crypto_generichash_state* state,
const unsigned char* in,
unsigned long long inlen) __attribute__((nonnull(1)));
int crypto_generichash_final(crypto_generichash_state* state, unsigned char* out,
const size_t outlen) __attribute__((nonnull));
#define crypto_sign_SECRETKEYBYTES 64
#define crypto_sign_PUBLICKEYBYTES 32
#define crypto_sign_BYTES 64
int crypto_sign_keypair(unsigned char* pk, unsigned char* sk) __attribute__((nonnull));
int crypto_sign_detached(unsigned char* sig, unsigned long long* siglen_p, const unsigned char* m,
unsigned long long mlen, const unsigned char* sk)
__attribute__((nonnull(1, 5)));
int crypto_sign_verify_detached(const unsigned char* sig,
const unsigned char* m,
unsigned long long mlen,
const unsigned char* pk) __attribute__((warn_unused_result))
__attribute__((nonnull(1, 4)));

View file

@ -1,18 +1,20 @@
<!---
This man page can be generated using ronn - http://rtomayko.github.com/ronn/
This man page can be generated using ronn - https://rtomayko.github.io/ronn/
-->
minisign(1) -- A dead simple tool to sign files and verify signatures.
======================================================================
# minisign(1) -- A dead simple tool to sign files and verify signatures.
## SYNOPSIS
`minisign` -G [-p pubkey] [-s seckey]
`minisign` -G [-p pubkey_file] [-s seckey_file] [-W]
`minisign` -S [-H] [-x sigfile] [-s seckey] [-c untrusted_comment] [-t trusted_comment] -m file [file ...]
`minisign` -R [-s seckey_file] [-p pubkey_file]
`minisign` -V [-x sigfile] [-p pubkeyfile | -P pubkey] [-o] [-q] -m file
`minisign` -C [-s seckey_file] [-W]
`minisign` -R -s seckey -p pubkeyfile
`minisign` -S [-H] [-x sig_file] [-s seckey_file] [-c untrusted_comment] [-t trusted_comment] -m file [file ...]
`minisign` -V [-x sig_file] [-p pubkey_file | -P pubkey] [-o] [-q] -m file
## DESCRIPTION
@ -24,41 +26,46 @@ It is portable, lightweight, and uses the highly secure [Ed25519](http://ed25519
These options control the actions of `minisign`.
* `-G`:
Generate a new key pair
* `-S`:
Sign files
* `-V`:
Verify that a signature is valid for a given file
* `-m <file>`:
File to sign/verify
* `-o`:
Combined with -V, output the file content after verification
* `-H`:
Combined with -S, pre-hash in order to sign large files
* `-p <pubkeyfile>`:
Public key file (default: ./minisign.pub)
* `-P <pubkey>`:
Public key, as a base64 string
* `-s <seckey>`:
Secret key file (default: ~/.minisign/minisign.key)
* `-x <sigfile>`:
Signature file (default: &lt;file&gt;.minisig)
* `-c <comment>`:
Add a one-line untrusted comment
* `-t <comment>`:
Add a one-line trusted comment
* `-q`:
Quiet mode, suppress output
* `-Q`:
Pretty quiet mode, only print the trusted comment
* `-R`:
Recreate a public key file from a secret key file
* `-f`:
Force. Combined with -G, overwrite a previous key pair
* `-v`:
Display version number
- `-G`:
Generate a new key pair
- `-C`:
Change/remove the password of a secret key
- `-R`:
Recreate a public key file from a secret key file
- `-S`:
Sign files
- `-V`:
Verify that a signature is valid for a given file
- `-H`:
Requires the input to be prehashed
- `-l`:
Sign using the legacy format
- `-m <file>`:
File to sign/verify
- `-o`:
Combined with -V, output the file content after verification
- `-p <pubkey_file>`:
Public key file (default: ./minisign.pub)
- `-P <pubkey>`:
Public key, as a base64 string
- `-s <seckey_file>`:
Secret key file (default: ~/.minisign/minisign.key)
- `-W`:
Do not encrypt/decrypt the secret key with a password
- `-x <sig_file>`:
Signature file (default: &lt;file&gt;.minisig)
- `-c <comment>`:
Add a one-line untrusted comment
- `-t <comment>`:
Add a one-line trusted comment
- `-q`:
Quiet mode, suppress output
- `-Q`:
Pretty quiet mode, only print the trusted comment
- `-f`:
Force. Combined with -G, overwrite a previous key pair
- `-v`:
Display version number
## EXAMPLES
@ -71,7 +78,7 @@ The public key is printed and put into the `minisign.pub` file. The secret key i
Signing files
$ `minisign` -Sm myfile.txt
$ `minisign` -Sm myfile.txt myfile2.txt *.c
$ `minisign` -Sm myfile.txt myfile2.txt \*.c
Or to include a comment in the signature, that will be verified and displayed when verifying the file:
@ -81,7 +88,7 @@ The secret key is loaded from `${MINISIGN_CONFIG_DIR}/minisign.key`, `~/.minisig
Verifying a file
$ `minisign` -Vm myfile.txt -P &lt;pubkey&gt;
$ `minisign` -Vm myfile.txt -P &lt;pubkey&gt;
or
@ -91,39 +98,15 @@ This requires the signature `myfile.txt.minisig` to be present in the same direc
The public key can either reside in a file (`./minisign.pub` by default) or be directly specified on the command line.
## Notes
## NOTES
**Trusted comments**
Signature files include an untrusted comment line that can be freely modified even after the signature is created.
Signature files include an untrusted comment line that can be freely modified, even after signature creation.
They also include a second comment line that cannot be modified without the secret key.
They also include a second comment line, that cannot be modified without the secret key.
Trusted comments can be used to add instructions or application-specific metadata such as the intended file name, timestamps, resource identifiers, or version numbers to prevent downgrade attacks.
Trusted comments can be used to add instructions or application-specific metadata (intended file name, timestamps, resource identifiers, version numbers to prevent downgrade attacks).
**Compatibility with OpenBSD signify**
Signatures written by `minisign` can be verified using OpenBSD's `signify` tool: public key files and signature files are compatible.
However, `minisign` uses a slightly different format to store secret keys.
`Minisign` signatures include trusted comments in addition to untrusted comments. Trusted comments are signed, thus verified, before being displayed.
This adds two lines to the signature files, that signify silently ignores.
**Pre-hashing**
By default, signing and verification require as much memory as the size of the file.
Since `Minisign 0.6`, huge files can be signed and verified with very low memory requirements, by pre-hashing the content.
The -H command-line switch, in combination with -S, generates a pre-hashed signature (HashEdDSA):
$ `minisign` -SHm myfile.txt
Verification of such a signature doesn't require any specific switch: the appropriate algorithm will automatically be detected.
Signatures generated that way are not compatible with OpenBSD's `signify` tool and are not compatible with `Minisign` versions prior to 0.6.
OpenBSD's `signify(1)` is conceptually similar to Minisign. Minisign creates signatures that can be verified by `signify`; however, signatures created by `signify` cannot be verified with Minisign because Minisign expects a trusted comment section to be present. Trusted comments are crucial for describing what has been signed, in addition to merely confirming that a signature exists.
## AUTHOR

View file

@ -10,7 +10,11 @@
#include <time.h>
#include <unistd.h>
#include <sodium.h>
#ifdef LIBZODIUM
# include "libzodium/sodium.h"
#else
# include <sodium.h>
#endif
#include "base64.h"
#include "get_line.h"
@ -18,7 +22,7 @@
#include "minisign.h"
#ifndef VERIFY_ONLY
static const char *getopt_options = "GSVRHhc:flm:oP:p:qQs:t:vx:";
static const char *getopt_options = "CGSVRHhc:flm:oP:p:qQs:t:vWx:";
#else
static const char *getopt_options = "VhHm:oP:p:qQvx:";
#endif
@ -31,30 +35,30 @@ usage(void)
puts(
"Usage:\n"
#ifndef VERIFY_ONLY
"minisign -G [-p pubkey] [-s seckey]\n"
"minisign -S [-l] [-x sigfile] [-s seckey] [-c untrusted_comment] [-t trusted_comment] -m "
"file [file ...]\n"
#endif
"minisign -V [-H] [-x sigfile] [-p pubkeyfile | -P pubkey] [-o] [-q] -m file\n"
#ifndef VERIFY_ONLY
"minisign -R -s seckey -p pubkeyfile\n"
"minisign -G [-f] [-p pubkey_file] [-s seckey_file] [-W]\n"
"minisign -R [-s seckey_file] [-p pubkey_file]\n"
"minisign -C [-s seckey_file] [-W]\n"
"minisign -S [-l] [-x sig_file] [-s seckey_file] [-c untrusted_comment]\n"
" [-t trusted_comment] -m file [file ...]\n"
#endif
"minisign -V [-H] [-x sig_file] [-p pubkey_file | -P pubkey] [-o] [-q] -m file\n"
"\n"
#ifndef VERIFY_ONLY
"-G generate a new key pair\n"
#endif
"-H require input to be prehashed\n"
#ifndef VERIFY_ONLY
"-R recreate a public key file from a secret key file\n"
"-C change/remove the password of the secret key\n"
"-S sign files\n"
#endif
"-V verify that a signature is valid for a given file\n"
"-H require input to be prehashed\n"
"-l sign using the legacy format\n"
"-m <file> file to sign/verify\n"
"-o combined with -V, output the file content after verification\n"
"-p <pubkeyfile> public key file (default: ./minisign.pub)\n"
"-p <pubkey_file> public key file (default: ./minisign.pub)\n"
"-P <pubkey> public key, as a base64 string\n"
#ifndef VERIFY_ONLY
"-s <seckey> secret key file (default: ~/.minisign/minisign.key)\n"
"-s <seckey_file> secret key file (default: ~/.minisign/minisign.key)\n"
"-W do not encrypt/decrypt the secret key with a password\n"
#endif
"-x <sigfile> signature file (default: <file>.minisig)\n"
#ifndef VERIFY_ONLY
@ -63,9 +67,6 @@ usage(void)
#endif
"-q quiet mode, suppress output\n"
"-Q pretty quiet mode, only print the trusted comment\n"
#ifndef VERIFY_ONLY
"-R recreate a public key file from a secret key file\n"
#endif
"-f force. Combined with -G, overwrite a previous key pair\n"
"-v display version number\n");
exit(2);
@ -76,8 +77,8 @@ message_load_hashed(size_t *message_len, const char *message_file)
{
crypto_generichash_state hs;
unsigned char buf[65536U];
unsigned char * message;
FILE * fp;
unsigned char *message;
FILE *fp;
size_t n;
if ((fp = fopen(message_file, "rb")) == NULL) {
@ -101,7 +102,7 @@ message_load_hashed(size_t *message_len, const char *message_file)
static unsigned char *
message_load(size_t *message_len, const char *message_file, int hashed)
{
FILE * fp;
FILE *fp;
unsigned char *message;
off_t message_len_;
@ -130,7 +131,7 @@ static int
output_file(const char *message_file)
{
unsigned char buf[65536U];
FILE * fp;
FILE *fp;
size_t n;
if ((fp = fopen(message_file, "rb")) == NULL) {
@ -155,9 +156,9 @@ sig_load(const char *sig_file, unsigned char global_sig[crypto_sign_BYTES], int
{
char comment[COMMENTMAXBYTES];
SigStruct *sig_struct;
FILE * fp;
char * global_sig_s;
char * sig_s;
FILE *fp;
char *global_sig_s;
char *sig_s;
size_t global_sig_len;
size_t global_sig_s_size;
size_t sig_s_size;
@ -169,6 +170,9 @@ sig_load(const char *sig_file, unsigned char global_sig[crypto_sign_BYTES], int
if (fgets(comment, (int) sizeof comment, fp) == NULL) {
exit_msg("Error while reading the signature file");
}
if (trim(comment) == 0) {
exit_msg("Untrusted signature comment too long");
}
if (strncmp(comment, COMMENT_PREFIX, (sizeof COMMENT_PREFIX) - 1U) != 0) {
exit_msg(
"Untrusted signature comment should start with "
@ -179,7 +183,9 @@ sig_load(const char *sig_file, unsigned char global_sig[crypto_sign_BYTES], int
if (fgets(sig_s, (int) sig_s_size, fp) == NULL) {
exit_msg("Error while reading the signature file");
}
trim(sig_s);
if (trim(sig_s) == 0) {
exit_msg("Signature too long");
}
if (fgets(trusted_comment, (int) trusted_comment_maxlen, fp) == NULL) {
exit_msg("Trusted comment not present");
}
@ -192,7 +198,9 @@ sig_load(const char *sig_file, unsigned char global_sig[crypto_sign_BYTES], int
memmove(trusted_comment,
trusted_comment + sizeof TRUSTED_COMMENT_PREFIX - 1U,
strlen(trusted_comment + sizeof TRUSTED_COMMENT_PREFIX - 1U) + 1U);
trim(trusted_comment);
if (trim(trusted_comment) == 0) {
exit_msg("Trusted comment too long");
}
global_sig_s_size = B64_MAX_LEN_FROM_BIN_LEN(crypto_sign_BYTES) + 2U;
global_sig_s = xmalloc(global_sig_s_size);
if (fgets(global_sig_s, (int) global_sig_s_size, fp) == NULL) {
@ -248,8 +256,8 @@ pubkey_load_file(const char *pk_file)
{
char pk_comment[COMMENTMAXBYTES];
PubkeyStruct *pubkey_struct;
FILE * fp;
char * pubkey_s = NULL;
FILE *fp;
char *pubkey_s = NULL;
size_t pubkey_s_size;
if ((fp = fopen(pk_file, "r")) == NULL) {
@ -285,8 +293,9 @@ pubkey_load(const char *pk_file, const char *pubkey_s)
exit_msg("A public key is required");
}
#ifndef VERIFY_ONLY
static void
seckey_chk(unsigned char chk[crypto_generichash_BYTES], const SeckeyStruct *seckey_struct)
seckey_compute_chk(unsigned char chk[crypto_generichash_BYTES], const SeckeyStruct *seckey_struct)
{
crypto_generichash_state hs;
@ -298,50 +307,12 @@ seckey_chk(unsigned char chk[crypto_generichash_BYTES], const SeckeyStruct *seck
crypto_generichash_final(&hs, chk, sizeof seckey_struct->keynum_sk.chk);
}
#ifndef VERIFY_ONLY
static SeckeyStruct *
seckey_load(const char *sk_file)
static void
decrypt_key(SeckeyStruct *const seckey_struct, unsigned char chk[crypto_generichash_BYTES])
{
char sk_comment[COMMENTMAXBYTES];
unsigned char chk[crypto_generichash_BYTES];
SeckeyStruct * seckey_struct;
FILE * fp;
char * pwd = xsodium_malloc(PASSWORDMAXBYTES);
char * seckey_s;
char *pwd = xsodium_malloc(PASSWORDMAXBYTES);
unsigned char *stream;
size_t seckey_s_size;
size_t seckey_struct_len;
if ((fp = fopen(sk_file, "r")) == NULL) {
exit_err(sk_file);
}
if (fgets(sk_comment, (int) sizeof sk_comment, fp) == NULL) {
exit_msg("Error while loading the secret key file");
}
sodium_memzero(sk_comment, sizeof sk_comment);
seckey_s_size = B64_MAX_LEN_FROM_BIN_LEN(sizeof *seckey_struct) + 2U;
seckey_s = xsodium_malloc(seckey_s_size);
seckey_struct = xsodium_malloc(sizeof *seckey_struct);
if (fgets(seckey_s, (int) seckey_s_size, fp) == NULL) {
exit_msg("Error while loading the secret key file");
}
trim(seckey_s);
xfclose(fp);
if (b64_to_bin((unsigned char *) (void *) seckey_struct, seckey_s, sizeof *seckey_struct,
strlen(seckey_s), &seckey_struct_len) == NULL ||
seckey_struct_len != sizeof *seckey_struct) {
exit_msg("base64 conversion failed - was an actual secret key given?");
}
sodium_free(seckey_s);
if (memcmp(seckey_struct->sig_alg, SIGALG, sizeof seckey_struct->sig_alg) != 0) {
exit_msg("Unsupported signature algorithm");
}
if (memcmp(seckey_struct->kdf_alg, KDFALG, sizeof seckey_struct->kdf_alg) != 0) {
exit_msg("Unsupported key derivation function");
}
if (memcmp(seckey_struct->chk_alg, CHKALG, sizeof seckey_struct->chk_alg) != 0) {
exit_msg("Unsupported checksum function");
}
if (get_password(pwd, PASSWORDMAXBYTES, "Password: ") != 0) {
exit_msg("get_password()");
}
@ -359,12 +330,109 @@ seckey_load(const char *sk_file)
sizeof seckey_struct->keynum_sk);
sodium_free(stream);
puts("done\n");
seckey_chk(chk, seckey_struct);
if (memcmp(chk, seckey_struct->keynum_sk.chk, sizeof chk) != 0) {
seckey_compute_chk(chk, seckey_struct);
if (memcmp(chk, seckey_struct->keynum_sk.chk, crypto_generichash_BYTES) != 0) {
exit_msg("Wrong password for that key");
}
sodium_memzero(chk, sizeof chk);
sodium_memzero(chk, crypto_generichash_BYTES);
}
static void
encrypt_key(SeckeyStruct *const seckey_struct)
{
char *pwd = xsodium_malloc(PASSWORDMAXBYTES);
char *pwd2 = xsodium_malloc(PASSWORDMAXBYTES);
unsigned char *stream;
unsigned long kdf_memlimit;
unsigned long kdf_opslimit;
puts("Please enter a password to protect the secret key.\n");
if (get_password(pwd, PASSWORDMAXBYTES, "Password: ") != 0 ||
get_password(pwd2, PASSWORDMAXBYTES, "Password (one more time): ") != 0) {
exit_msg("get_password()");
}
if (strcmp(pwd, pwd2) != 0) {
exit_msg("Passwords don't match");
}
printf("Deriving a key from the password in order to encrypt the secret key... ");
fflush(stdout);
stream = xsodium_malloc(sizeof seckey_struct->keynum_sk);
randombytes_buf(seckey_struct->kdf_salt, sizeof seckey_struct->kdf_salt);
kdf_opslimit = crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_SENSITIVE;
kdf_memlimit = crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_SENSITIVE;
while (crypto_pwhash_scryptsalsa208sha256(stream, sizeof seckey_struct->keynum_sk, pwd,
strlen(pwd), seckey_struct->kdf_salt, kdf_opslimit,
kdf_memlimit) != 0) {
kdf_opslimit /= 2;
kdf_memlimit /= 2;
if (kdf_opslimit < crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN ||
kdf_memlimit < crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN) {
exit_err("Unable to complete key derivation - More memory would be needed");
}
}
sodium_free(pwd);
sodium_free(pwd2);
if (kdf_memlimit < crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_SENSITIVE) {
fprintf(stderr,
"Warning: due to limited memory the KDF used less "
"memory than the default\n");
}
le64_store(seckey_struct->kdf_opslimit_le, kdf_opslimit);
le64_store(seckey_struct->kdf_memlimit_le, kdf_memlimit);
seckey_compute_chk(seckey_struct->keynum_sk.chk, seckey_struct);
xor_buf((unsigned char *) (void *) &seckey_struct->keynum_sk, stream,
sizeof seckey_struct->keynum_sk);
sodium_free(stream);
puts("done\n");
}
static SeckeyStruct *
seckey_load(const char *sk_file, char *const sk_comment_line)
{
char sk_comment_line_buf[COMMENTMAXBYTES];
unsigned char chk[crypto_generichash_BYTES];
SeckeyStruct *seckey_struct;
FILE *fp;
char *seckey_s;
size_t seckey_s_size;
size_t seckey_struct_len;
if ((fp = fopen(sk_file, "r")) == NULL) {
exit_err(sk_file);
}
if (fgets(sk_comment_line_buf, (int) sizeof sk_comment_line_buf, fp) == NULL) {
exit_msg("Error while loading the secret key file");
}
if (sk_comment_line != NULL) {
memcpy(sk_comment_line, sk_comment_line_buf, sizeof sk_comment_line_buf);
}
sodium_memzero(sk_comment_line_buf, sizeof sk_comment_line_buf);
seckey_s_size = B64_MAX_LEN_FROM_BIN_LEN(sizeof *seckey_struct) + 2U;
seckey_s = xsodium_malloc(seckey_s_size);
seckey_struct = xsodium_malloc(sizeof *seckey_struct);
if (fgets(seckey_s, (int) seckey_s_size, fp) == NULL) {
exit_msg("Error while loading the secret key file");
}
trim(seckey_s);
xfclose(fp);
if (b64_to_bin((unsigned char *) (void *) seckey_struct, seckey_s, sizeof *seckey_struct,
strlen(seckey_s), &seckey_struct_len) == NULL ||
seckey_struct_len != sizeof *seckey_struct) {
exit_msg("base64 conversion failed - was an actual secret key given?");
}
sodium_free(seckey_s);
if (memcmp(seckey_struct->sig_alg, SIGALG, sizeof seckey_struct->sig_alg) != 0) {
exit_msg("Unsupported signature algorithm");
}
if (memcmp(seckey_struct->chk_alg, CHKALG, sizeof seckey_struct->chk_alg) != 0) {
exit_msg("Unsupported checksum function");
}
if (memcmp(seckey_struct->kdf_alg, KDFALG, sizeof seckey_struct->kdf_alg) == 0) {
decrypt_key(seckey_struct, chk);
} else if (memcmp(seckey_struct->kdf_alg, KDFNONE, sizeof seckey_struct->kdf_alg) != 0) {
exit_msg("Unsupported key derivation function");
}
return seckey_struct;
}
#endif
@ -375,9 +443,9 @@ verify(PubkeyStruct *pubkey_struct, const char *message_file, const char *sig_fi
{
char trusted_comment[TRUSTEDCOMMENTMAXBYTES];
unsigned char global_sig[crypto_sign_BYTES];
FILE * info_fp = stdout;
FILE *info_fp = stdout;
unsigned char *sig_and_trusted_comment;
SigStruct * sig_struct;
SigStruct *sig_struct;
unsigned char *message;
size_t message_len;
size_t trusted_comment_len;
@ -397,9 +465,9 @@ verify(PubkeyStruct *pubkey_struct, const char *message_file, const char *sig_fi
if (memcmp(sig_struct->keynum, pubkey_struct->keynum_pk.keynum, sizeof sig_struct->keynum) !=
0) {
fprintf(stderr,
"Signature key id in %s is %" PRIX64
"Signature key id in %s is %016" PRIX64
"\n"
"but the key id in the public key is %" PRIX64 "\n",
"but the key id in the public key is %016" PRIX64 "\n",
sig_file, le64_load(sig_struct->keynum),
le64_load(pubkey_struct->keynum_pk.keynum));
exit(1);
@ -445,7 +513,7 @@ verify(PubkeyStruct *pubkey_struct, const char *message_file, const char *sig_fi
static char *
append_sig_suffix(const char *message_file)
{
char * sig_file;
char *sig_file;
size_t message_file_len = strlen(message_file);
sig_file = xmalloc(message_file_len + sizeof SIG_SUFFIX);
@ -459,7 +527,7 @@ append_sig_suffix(const char *message_file)
static char *
default_trusted_comment(const char *message_file, int hashed)
{
char * ret;
char *ret;
time_t ts = time(NULL);
if (asprintf(&ret, "timestamp:%lu\tfile:%s%s", (unsigned long) ts, file_basename(message_file),
@ -476,10 +544,10 @@ sign(SeckeyStruct *seckey_struct, PubkeyStruct *pubkey_struct, const char *messa
{
unsigned char global_sig[crypto_sign_BYTES];
SigStruct sig_struct;
FILE * fp;
FILE *fp;
unsigned char *message;
unsigned char *sig_and_trusted_comment;
char * tmp_trusted_comment = NULL;
char *tmp_trusted_comment = NULL;
size_t comment_len;
size_t trusted_comment_len;
size_t message_len;
@ -598,74 +666,34 @@ write_pk_file(const char *pk_file, const PubkeyStruct *pubkey_struct)
if ((fp = fopen(pk_file, "w")) == NULL) {
exit_err(pk_file);
}
xfprintf(fp, COMMENT_PREFIX "minisign public key %" PRIX64 "\n",
xfprintf(fp, COMMENT_PREFIX "minisign public key %016" PRIX64 "\n",
le64_load(pubkey_struct->keynum_pk.keynum));
xfput_b64(fp, (const unsigned char *) (const void *) pubkey_struct, sizeof *pubkey_struct);
xfclose(fp);
}
static int
generate(const char *pk_file, const char *sk_file, const char *comment, int force)
generate(const char *pk_file, const char *sk_file, const char *comment, int force,
int unencrypted_key)
{
char * pwd = xsodium_malloc(PASSWORDMAXBYTES);
char * pwd2 = xsodium_malloc(PASSWORDMAXBYTES);
SeckeyStruct * seckey_struct = xsodium_malloc(sizeof(SeckeyStruct));
PubkeyStruct * pubkey_struct = xsodium_malloc(sizeof(PubkeyStruct));
unsigned char *stream;
FILE * fp;
unsigned long kdf_memlimit;
unsigned long kdf_opslimit;
SeckeyStruct *seckey_struct = xsodium_malloc(sizeof(SeckeyStruct));
PubkeyStruct *pubkey_struct = xsodium_malloc(sizeof(PubkeyStruct));
FILE *fp;
abort_on_existing_key_files(pk_file, sk_file, force);
memset(seckey_struct, 0, sizeof(SeckeyStruct));
randombytes_buf(seckey_struct->keynum_sk.keynum, sizeof seckey_struct->keynum_sk.keynum);
crypto_sign_keypair(pubkey_struct->keynum_pk.pk, seckey_struct->keynum_sk.sk);
memcpy(seckey_struct->sig_alg, SIGALG, sizeof seckey_struct->sig_alg);
memcpy(seckey_struct->kdf_alg, KDFALG, sizeof seckey_struct->kdf_alg);
memcpy(seckey_struct->kdf_alg, unencrypted_key ? KDFNONE : KDFALG,
sizeof seckey_struct->kdf_alg);
memcpy(seckey_struct->chk_alg, CHKALG, sizeof seckey_struct->chk_alg);
memcpy(pubkey_struct->keynum_pk.keynum, seckey_struct->keynum_sk.keynum,
sizeof pubkey_struct->keynum_pk.keynum);
memcpy(pubkey_struct->sig_alg, SIGALG, sizeof pubkey_struct->sig_alg);
puts("Please enter a password to protect the secret key.\n");
if (get_password(pwd, PASSWORDMAXBYTES, "Password: ") != 0 ||
get_password(pwd2, PASSWORDMAXBYTES, "Password (one more time): ") != 0) {
exit_msg("get_password()");
if (unencrypted_key == 0) {
encrypt_key(seckey_struct);
}
if (strcmp(pwd, pwd2) != 0) {
exit_msg("Passwords don't match");
}
printf("Deriving a key from the password in order to encrypt the secret key... ");
fflush(stdout);
stream = xsodium_malloc(sizeof seckey_struct->keynum_sk);
randombytes_buf(seckey_struct->kdf_salt, sizeof seckey_struct->kdf_salt);
kdf_opslimit = crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_SENSITIVE;
kdf_memlimit = crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_SENSITIVE;
while (crypto_pwhash_scryptsalsa208sha256(stream, sizeof seckey_struct->keynum_sk, pwd,
strlen(pwd), seckey_struct->kdf_salt, kdf_opslimit,
kdf_memlimit) != 0) {
kdf_opslimit /= 2;
kdf_memlimit /= 2;
if (kdf_opslimit < crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN ||
kdf_memlimit < crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN) {
exit_err("Unable to complete key derivation - More memory would be needed");
}
}
sodium_free(pwd);
sodium_free(pwd2);
if (kdf_memlimit < crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_SENSITIVE) {
fprintf(stderr,
"Warning: due to limited memory the KDF used less "
"memory than the default\n");
}
le64_store(seckey_struct->kdf_opslimit_le, kdf_opslimit);
le64_store(seckey_struct->kdf_memlimit_le, kdf_memlimit);
seckey_chk(seckey_struct->keynum_sk.chk, seckey_struct);
xor_buf((unsigned char *) (void *) &seckey_struct->keynum_sk, stream,
sizeof seckey_struct->keynum_sk);
sodium_free(stream);
puts("done\n");
abort_on_existing_key_files(pk_file, sk_file, force);
if (basedir_create_useronly(sk_file) != 0) {
fprintf(stderr, "Warning: you may have to create the parent directory\n");
@ -700,7 +728,7 @@ recreate_pk(const char *pk_file, const char *sk_file, int force)
if (force == 0) {
abort_on_existing_key_file(pk_file);
}
if ((seckey_struct = seckey_load(sk_file)) == NULL) {
if ((seckey_struct = seckey_load(sk_file, NULL)) == NULL) {
return -1;
}
memcpy(pubkey_struct.sig_alg, seckey_struct->sig_alg, sizeof pubkey_struct.sig_alg);
@ -718,6 +746,43 @@ recreate_pk(const char *pk_file, const char *sk_file, int force)
return 0;
}
static int
update_password(const char *sk_file, int unencrypted_key)
{
SeckeyStruct *seckey_struct;
char *sk_comment_line;
FILE *fp;
if (unencrypted_key != 0) {
printf("Key encryption for [%s] is going to be removed.\n", sk_file);
}
sk_comment_line = xsodium_malloc(COMMENTMAXBYTES);
if ((seckey_struct = seckey_load(sk_file, sk_comment_line)) == NULL) {
return -1;
}
memcpy(seckey_struct->kdf_alg, unencrypted_key ? KDFNONE : KDFALG,
sizeof seckey_struct->kdf_alg);
if (unencrypted_key == 0) {
encrypt_key(seckey_struct);
}
if ((fp = fopen_create_useronly(sk_file)) == NULL) {
exit_err(sk_file);
}
trim(sk_comment_line);
xfprintf(fp, "%s\n", sk_comment_line);
sodium_free(sk_comment_line);
xfput_b64(fp, (unsigned char *) (void *) seckey_struct, sizeof *seckey_struct);
xfclose(fp);
sodium_free(seckey_struct);
if (unencrypted_key == 0) {
puts("Password updated.");
} else {
puts("Password removed.");
}
return 0;
}
#endif
#ifndef VERIFY_ONLY
@ -725,8 +790,8 @@ static char *
sig_config_dir(void)
{
const char *config_dir_env;
char * config_dir;
char * home_dir;
char *config_dir;
char *home_dir;
config_dir = NULL;
if ((config_dir_env = getenv(SIG_DEFAULT_CONFIG_DIR_ENV_VAR)) != NULL) {
@ -768,19 +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 * comment = NULL;
const char * pubkey_s = NULL;
const char * trusted_comment = NULL;
unsigned char opt_seen[16] = { 0 };
const char *sig_file = NULL;
const char *message_file = NULL;
#ifndef VERIFY_ONLY
const char *comment = NULL;
#endif
const char *pubkey_s = NULL;
#ifndef VERIFY_ONLY
const char *trusted_comment = NULL;
#endif
unsigned char opt_seen[16] = { 0 };
int opt_flag;
int quiet = 0;
int output = 0;
int force = 0;
int allow_legacy = 1;
int sign_legacy = 0;
Action action = ACTION_NONE;
int quiet = 0;
int output = 0;
#ifndef VERIFY_ONLY
int force = 0;
#endif
int allow_legacy = 1;
#ifndef VERIFY_ONLY
int sign_legacy = 0;
int unencrypted_key = 0;
#endif
Action action = ACTION_NONE;
while ((opt_flag = getopt(argc, argv, getopt_options)) != -1) {
switch (opt_flag) {
@ -797,6 +871,12 @@ main(int argc, char **argv)
}
action = ACTION_SIGN;
break;
case 'C':
if (action != ACTION_NONE && action != ACTION_UPDATE_PASSWORD) {
usage();
}
action = ACTION_UPDATE_PASSWORD;
break;
case 'R':
if (action != ACTION_NONE && action != ACTION_RECREATE_PK) {
usage();
@ -823,9 +903,11 @@ main(int argc, char **argv)
case 'H':
allow_legacy = 0;
break;
#ifndef VERIFY_ONLY
case 'l':
sign_legacy = 1;
break;
#endif
case 'm':
message_file = optarg;
break;
@ -852,6 +934,9 @@ main(int argc, char **argv)
case 't':
trusted_comment = optarg;
break;
case 'W':
unencrypted_key = 1;
break;
#endif
case 'x':
sig_file = optarg;
@ -883,7 +968,7 @@ main(int argc, char **argv)
if (pk_file == NULL) {
pk_file = SIG_DEFAULT_PKFILE;
}
return generate(pk_file, sk_file, comment, force) != 0;
return generate(pk_file, sk_file, comment, force, unencrypted_key) != 0;
case ACTION_SIGN:
if (message_file == NULL) {
usage();
@ -895,7 +980,7 @@ main(int argc, char **argv)
comment = DEFAULT_COMMENT;
}
return sign_all(
seckey_load(sk_file),
seckey_load(sk_file, NULL),
((pk_file != NULL || pubkey_s != NULL) ? pubkey_load(pk_file, pubkey_s) : NULL),
message_file, (const char **) &argv[optind], argc - optind, sig_file, comment,
trusted_comment, sign_legacy) != 0;
@ -904,6 +989,8 @@ main(int argc, char **argv)
pk_file = SIG_DEFAULT_PKFILE;
}
return recreate_pk(pk_file, sk_file, force) != 0;
case ACTION_UPDATE_PASSWORD:
return update_password(sk_file, unencrypted_key) != 0;
#endif
case ACTION_VERIFY:
if (message_file == NULL) {

View file

@ -9,6 +9,7 @@
#define SIGALG "Ed"
#define SIGALG_HASHED "ED"
#define KDFALG "Sc"
#define KDFNONE "\0\0"
#define CHKALG "B2"
#define COMMENT_PREFIX "untrusted comment: "
#define DEFAULT_COMMENT "signature from minisign secret key"
@ -19,7 +20,7 @@
#define SIG_DEFAULT_PKFILE "minisign.pub"
#define SIG_DEFAULT_SKFILE "minisign.key"
#define SIG_SUFFIX ".minisig"
#define VERSION_STRING "minisign 0.10"
#define VERSION_STRING "minisign 0.12"
typedef struct KeynumSK_ {
unsigned char keynum[KEYNUMBYTES];
@ -58,7 +59,8 @@ typedef enum Action_ {
ACTION_GENERATE,
ACTION_SIGN,
ACTION_VERIFY,
ACTION_RECREATE_PK
ACTION_RECREATE_PK,
ACTION_UPDATE_PASSWORD
} Action;
#endif