pax_global_header00006660000000000000000000000064147420263160014517gustar00rootroot0000000000000052 comment=b85e15d45ac9eab34e44596fd309f5b07db9545c minisign-0.12/000077500000000000000000000000001474202631600132565ustar00rootroot00000000000000minisign-0.12/.clang-format000066400000000000000000000106531474202631600156360ustar00rootroot00000000000000--- Language: Cpp AccessModifierOffset: -4 AlignAfterOpenBracket: Align AlignConsecutiveMacros: true AlignConsecutiveAssignments: true AlignConsecutiveBitFields: true AlignConsecutiveDeclarations: true AlignEscapedNewlines: true AlignOperands: true AlignTrailingComments: false AllowAllArgumentsOnNextLine: true AllowAllConstructorInitializersOnNextLine: true AllowAllParametersOfDeclarationOnNextLine: true AllowShortEnumsOnASingleLine: true AllowShortBlocksOnASingleLine: false AllowShortCaseLabelsOnASingleLine: false AllowShortFunctionsOnASingleLine: false AllowShortLambdasOnASingleLine: All AllowShortIfStatementsOnASingleLine: Never AllowShortLoopsOnASingleLine: false AlwaysBreakAfterDefinitionReturnType: None AlwaysBreakAfterReturnType: TopLevelDefinitions AlwaysBreakBeforeMultilineStrings: true AlwaysBreakTemplateDeclarations: MultiLine AttributeMacros: - __capability BinPackArguments: true BinPackParameters: true BraceWrapping: AfterCaseLabel: false AfterClass: false AfterControlStatement: Never AfterEnum: false AfterFunction: true AfterNamespace: false AfterObjCDeclaration: false AfterStruct: false AfterUnion: false AfterExternBlock: false BeforeCatch: false BeforeElse: false BeforeLambdaBody: false BeforeWhile: false IndentBraces: false SplitEmptyFunction: true SplitEmptyRecord: true SplitEmptyNamespace: true BreakBeforeBinaryOperators: None BreakBeforeConceptDeclarations: true BreakBeforeBraces: WebKit BreakBeforeInheritanceComma: true BreakInheritanceList: BeforeColon BreakBeforeTernaryOperators: true BreakConstructorInitializersBeforeComma: false BreakConstructorInitializers: BeforeComma BreakAfterJavaFieldAnnotations: false BreakStringLiterals: true ColumnLimit: 100 CommentPragmas: "^ IWYU pragma:" CompactNamespaces: false ConstructorInitializerAllOnOneLineOrOnePerLine: false ConstructorInitializerIndentWidth: 4 ContinuationIndentWidth: 4 Cpp11BracedListStyle: false DeriveLineEnding: true DerivePointerAlignment: true DisableFormat: false EmptyLineBeforeAccessModifier: LogicalBlock ExperimentalAutoDetectBinPacking: true FixNamespaceComments: false ForEachMacros: - foreach - Q_FOREACH - BOOST_FOREACH StatementAttributeLikeMacros: - Q_EMIT IncludeBlocks: Preserve IncludeCategories: - Regex: '^"(llvm|llvm-c|clang|clang-c)/' Priority: 2 SortPriority: 0 CaseSensitive: false - Regex: '^(<|"(gtest|gmock|isl|json)/)' Priority: 3 SortPriority: 0 CaseSensitive: false - Regex: ".*" Priority: 1 SortPriority: 0 CaseSensitive: false IncludeIsMainRegex: "(Test)?$" IncludeIsMainSourceRegex: "" IndentCaseLabels: false IndentCaseBlocks: false IndentGotoLabels: true IndentPPDirectives: AfterHash IndentExternBlock: AfterExternBlock IndentRequires: false IndentWidth: 4 IndentWrappedFunctionNames: false InsertTrailingCommas: None JavaScriptQuotes: Leave JavaScriptWrapImports: true KeepEmptyLinesAtTheStartOfBlocks: true MacroBlockBegin: "" MacroBlockEnd: "" MaxEmptyLinesToKeep: 1 NamespaceIndentation: Inner ObjCBinPackProtocolList: Auto ObjCBlockIndentWidth: 4 ObjCBreakBeforeNestedBlockParam: true ObjCSpaceAfterProperty: true ObjCSpaceBeforeProtocolList: true PenaltyBreakAssignment: 2 PenaltyBreakBeforeFirstCallParameter: 19 PenaltyBreakComment: 300 PenaltyBreakFirstLessLess: 120 PenaltyBreakString: 1000 PenaltyBreakTemplateDeclaration: 10 PenaltyExcessCharacter: 1000000 PenaltyReturnTypeOnItsOwnLine: 60 PenaltyIndentedWhitespace: 0 PointerAlignment: Left ReflowComments: true SortIncludes: true SortJavaStaticImport: Before SortUsingDeclarations: true SpaceAfterCStyleCast: true SpaceAfterLogicalNot: false SpaceAfterTemplateKeyword: true SpaceBeforeAssignmentOperators: true SpaceBeforeCaseColon: false SpaceBeforeCpp11BracedList: true SpaceBeforeCtorInitializerColon: true SpaceBeforeInheritanceColon: true SpaceBeforeParens: ControlStatements SpaceAroundPointerQualifiers: Default SpaceBeforeRangeBasedForLoopColon: true SpaceInEmptyBlock: true SpaceInEmptyParentheses: false SpacesBeforeTrailingComments: 1 SpacesInAngles: false SpacesInConditionalStatement: false SpacesInContainerLiterals: true SpacesInCStyleCastParentheses: false SpacesInParentheses: false SpacesInSquareBrackets: false SpaceBeforeSquareBrackets: false BitFieldColonSpacing: Both Standard: Latest StatementMacros: - Q_UNUSED - QT_REQUIRE_VERSION TabWidth: 4 UseCRLF: false UseTab: Never WhitespaceSensitiveMacros: - STRINGIZE - PP_STRINGIZE - BOOST_PP_STRINGIZE - NS_SWIFT_NAME - CF_SWIFT_NAME --- minisign-0.12/.dockerignore000066400000000000000000000000671474202631600157350ustar00rootroot00000000000000** !LICENSE !README.md !/share !/src !/CMakeLists.txt minisign-0.12/.github/000077500000000000000000000000001474202631600146165ustar00rootroot00000000000000minisign-0.12/.github/workflows/000077500000000000000000000000001474202631600166535ustar00rootroot00000000000000minisign-0.12/.github/workflows/codeql-analysis.yml000066400000000000000000000011521474202631600224650ustar00rootroot00000000000000name: "CodeQL scan" on: push: pull_request: schedule: - cron: '0 16 * * 2' jobs: CodeQL-Build: runs-on: ubuntu-latest steps: - name: Checkout repository uses: actions/checkout@v2 with: fetch-depth: 2 - run: git checkout HEAD^2 if: ${{ github.event_name == 'pull_request' }} - run: sudo apt-get install -qy libsodium-dev - name: Initialize CodeQL uses: github/codeql-action/init@v1 - name: Autobuild uses: github/codeql-action/autobuild@v1 - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v1 minisign-0.12/.github/workflows/issues.yml000066400000000000000000000010121474202631600207030ustar00rootroot00000000000000name: 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 }} minisign-0.12/.gitignore000066400000000000000000000003261474202631600152470ustar00rootroot00000000000000*.dSYM *.exp *.gcda *.gcno *.la *.lo *.log *.mem *.o *.plist *.scan *.sdf *.status *.tar.* *~ .DS_Store .deps .dirstamp .done .libs CMakeCache.txt CMakeFiles Makefile cmake_install.cmake minisign zig-cache zig-out minisign-0.12/CMakeLists.txt000066400000000000000000000061431474202631600160220ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.10) project(minisign C) set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "minisign") 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 "12") set(CPACK_PACKAGE_VERSION_PATCH "0") set( CPACK_SOURCE_PACKAGE_FILE_NAME "${CMAKE_PROJECT_NAME}-${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}" ) set( CPACK_SOURCE_IGNORE_FILES "/build/;minisign.key;minisign.pub;a.out;/.git/;~$;${CPACK_SOURCE_IGNORE_FILES}" ) set(CPACK_PACKAGE_EXECUTABLES "minisign" "minisign") if(NOT CMAKE_BUILD_TYPE) set( CMAKE_BUILD_TYPE MinSizeRel CACHE STRING "Choose the type of build, options are: None Debug Release RelWithDebInfo MinSizeRel." FORCE) endif(NOT CMAKE_BUILD_TYPE) include(CPack) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_ALL_SOURCE -D_GNU_SOURCE") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64") set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake) if(BUILD_STATIC_EXECUTABLES) set(STATIC_LIBSODIUM on) if (NOT APPLE) set(CMAKE_EXE_LINKER_FLAGS -static) endif (NOT APPLE) endif(BUILD_STATIC_EXECUTABLES) add_executable(minisign src/base64.c src/get_line.c src/helpers.c src/minisign.c) find_package(PkgConfig REQUIRED) pkg_check_modules(LIBSODIUM libsodium) if(STATIC_LIBSODIUM) if(BUILD_STATIC_EXECUTABLES) set_target_properties(minisign PROPERTIES LINK_SEARCH_START_STATIC 1) set_target_properties(minisign PROPERTIES LINK_SEARCH_END_STATIC 1) endif() set(CMAKE_FIND_LIBRARY_SUFFIXES .lib .a ${CMAKE_FIND_LIBRARY_SUFFIXES}) endif() find_path(SODIUM_INCLUDE_DIR sodium.h HINTS ${LIBSODIUM_INCLUDE_DIRS} /usr/local/include /opt/local/include /opt/include) find_library(SODIUM_LIBRARY NAMES sodium HINTS ${LIBSODIUM_LIBRARY_DIRS} /usr/local/lib /opt/local/lib /opt/lib) if(STATIC_LIBSODIUM) set(LIBSODIUM_CFLAGS_OTHER ${LIBSODIUM_STATIC_CFLAGS_OTHER}) set(LIBSODIUM_LDFLAGS_OTHER ${LIBSODIUM_STATIC_LDFLAGS_OTHER}) endif() target_include_directories(minisign PUBLIC ${SODIUM_INCLUDE_DIR}) target_compile_options(minisign PUBLIC ${LIBSODIUM_CFLAGS} ${LIBSODIUM_CFLAGS_OTHER}) target_link_libraries(minisign ${SODIUM_LIBRARY} ${LIBSODIUM_LDFLAGS_OTHER}) set(THREADS_PREFER_PTHREAD_FLAG ON) find_package(Threads REQUIRED) if(THREADS_HAVE_PTHREAD_ARG) target_compile_options(minisign PUBLIC "-pthread") endif(THREADS_HAVE_PTHREAD_ARG) if(CMAKE_THREAD_LIBS_INIT) target_link_libraries(minisign "${CMAKE_THREAD_LIBS_INIT}") endif(CMAKE_THREAD_LIBS_INIT) if (NOT MSVC AND CMAKE_STRIP) add_custom_command(TARGET minisign POST_BUILD COMMAND ${CMAKE_STRIP} ${STRIP_FLAGS} $) endif(NOT MSVC AND CMAKE_STRIP) install(TARGETS minisign DESTINATION bin) include(GNUInstallDirs) install(FILES "share/man/man1/minisign.1" DESTINATION "${CMAKE_INSTALL_MANDIR}/man1" COMPONENT doc) minisign-0.12/Dockerfile000066400000000000000000000013121474202631600152450ustar00rootroot00000000000000FROM alpine:latest as build WORKDIR /usr/src/minisign RUN apk add --no-cache build-base cmake curl pkgconfig 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 -DCMAKE_BUILD_TYPE=MinSizeRel -DBUILD_STATIC_EXECUTABLES=1 .. && make -j$(nproc) RUN upx --lzma build/minisign ||: FROM scratch COPY --from=build /usr/src/minisign/build/minisign /usr/local/bin/ ENTRYPOINT ["/usr/local/bin/minisign"] minisign-0.12/LICENSE000066400000000000000000000014631474202631600142670ustar00rootroot00000000000000ISC LICENSE. /* * Copyright (c) 2015-2025 * Frank Denis * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ minisign-0.12/README.md000066400000000000000000000105211474202631600145340ustar00rootroot00000000000000![CodeQL scan](https://github.com/jedisct1/minisign/workflows/CodeQL%20scan/badge.svg) Minisign ======== Minisign is a dead simple tool to sign files and verify signatures. For more information, please refer to the [Minisign documentation](https://jedisct1.github.io/minisign/) Tarballs and pre-compiled binaries can be verified with the following public key: RWQf6LRCGA9i53mlYecO4IzT51TGPpvWucNSCh1CBM0QTaLn73Y7GFO3 Compilation / installation -------------------------- ## Building with Zig Dependencies: * [libsodium](https://libsodium.org/) (*optional*) * [zig](https://ziglang.org) Compilation with libsodium, dynamically linked (libsodium will need to be installed on the system for the command to run): $ zig build -Doptimize=ReleaseSmall Compilation with libsodium, statically linked (libsodium will only be needed for compilation): $ 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: $ mkdir build $ cd build $ cmake .. $ make # make install Alternative configuration for static binaries: $ cmake -D STATIC_LIBSODIUM=1 .. or: $ cmake -D BUILD_STATIC_EXECUTABLES=1 .. Minisign is also available in Homebrew: $ brew install minisign Minisign is also available in Scoop on Windows: $ scoop install minisign Minisign is also available in chocolatey on Windows: $ choco install minisign Minisign is also available with docker: $ docker run -i --rm jedisct1/minisign For example, verifying a signature using the docker image can be done with: $ docker run -v .:/minisign -e HOME=/minisign -w /minisign \ -it --rm jedisct1/minisign \ -Vm file_to_verify -p minisign.pub 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 adds random noise to the computation of EdDSA nonces. Other implementations can choose to use non-deterministic signatures by default. They will remain fully interoperable with implementations using deterministic signatures. minisign-0.12/build-dist-package.sh000077500000000000000000000001351474202631600172450ustar00rootroot00000000000000#! /bin/sh tar czpvf minisign-0.12.tar.gz $(git ls-files) minisign -Sm minisign-0.12.tar.gz minisign-0.12/build.zig000066400000000000000000000034361474202631600150760ustar00rootroot00000000000000const std = @import("std"); 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, }); minisign.linkLibC(); 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, .preferred_link_mode = if (use_static_linking) .static else .dynamic, }, ); } minisign.addIncludePath(b.path("src")); minisign.addSystemIncludePath(.{ .cwd_relative = "/opt/homebrew/include" }); minisign.addSystemIncludePath(.{ .cwd_relative = "/usr/local/include" }); 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); } minisign-0.12/build.zig.zon000066400000000000000000000003031474202631600156710ustar00rootroot00000000000000.{ .name = "minisign", .version = "0.12.0", .paths = .{ "LICEMSE", "README.md", "build.zig", "build.zig.zon", "src", "share", }, } minisign-0.12/cosign.pub000066400000000000000000000002621474202631600152500ustar00rootroot00000000000000-----BEGIN PUBLIC KEY----- MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAExjZWrlc6c58W7ZzmQnx6mugty99C OQTDtJeciX9LF9hEbs1J1fzZHRdRhV4OTqcq0jTW9PXnrSSZlk1fbkE/5w== -----END PUBLIC KEY----- minisign-0.12/share/000077500000000000000000000000001474202631600143605ustar00rootroot00000000000000minisign-0.12/share/man/000077500000000000000000000000001474202631600151335ustar00rootroot00000000000000minisign-0.12/share/man/man1/000077500000000000000000000000001474202631600157675ustar00rootroot00000000000000minisign-0.12/share/man/man1/minisign.1000066400000000000000000000070761474202631600177000ustar00rootroot00000000000000.\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . .TH "MINISIGN" "1" "January 2023" "" "" . .SH "NAME" \fBminisign\fR \- A dead simple tool to sign files and verify signatures\. . .SH "SYNOPSIS" \fBminisign\fR \-G [\-p pubkey_file] [\-s seckey_file] [\-W] . .P \fBminisign\fR \-R [\-s seckey_file] [\-p pubkey_file] . .P \fBminisign\fR \-C [\-s seckey_file] [\-W] . .P \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\. . .P It is portable, lightweight, and uses the highly secure Ed25519 \fIhttp://ed25519\.cr\.yp\.to/\fR public\-key signature system\. . .SH "OPTIONS" These options control the actions of \fBminisign\fR\. . .TP \fB\-G\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 . .TP \fB\-V\fR 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 \fR File to sign/verify . .TP \fB\-o\fR Combined with \-V, output the file content after verification . .TP \fB\-p \fR Public key file (default: \./minisign\.pub) . .TP \fB\-P \fR Public key, as a base64 string . .TP \fB\-s \fR Secret key file (default: ~/\.minisign/minisign\.key) . .TP \fB\-W\fR Do not encrypt/decrypt the secret key with a password . .TP \fB\-x \fR Signature file (default: \.minisig) . .TP \fB\-c \fR Add a one\-line untrusted comment . .TP \fB\-t \fR Add a one\-line trusted comment . .TP \fB\-q\fR Quiet mode, suppress output . .TP \fB\-Q\fR Pretty quiet mode, only print the trusted comment . .TP \fB\-f\fR Force\. Combined with \-G, overwrite a previous key pair . .TP \fB\-v\fR Display version number . .SH "EXAMPLES" Creating a key pair . .P \fBminisign\fR \-G . .P The public key is printed and put into the \fBminisign\.pub\fR file\. The secret key is encrypted and saved as a file named \fB~/\.minisign/minisign\.key\fR\. . .P Signing files . .P $ \fBminisign\fR \-Sm myfile\.txt $ \fBminisign\fR \-Sm myfile\.txt myfile2\.txt *\.c . .P Or to include a comment in the signature, that will be verified and displayed when verifying the file: . .P $ \fBminisign\fR \-Sm myfile\.txt \-t \'This comment will be signed as well\' . .P The secret key is loaded from \fB${MINISIGN_CONFIG_DIR}/minisign\.key\fR, \fB~/\.minisign/minisign\.key\fR, or its path can be explicitly set with the \fB\-s \fR command\-line switch\. . .P Verifying a file . .P $ \fBminisign\fR \-Vm myfile\.txt \-P . .P or . .P $ \fBminisign\fR \-Vm myfile\.txt \-p signature\.pub . .P This requires the signature \fBmyfile\.txt\.minisig\fR to be present in the same directory\. . .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" Signature files include an untrusted comment line that can be freely modified, even after signature creation\. . .P They also include a second comment line, that cannot be modified without the secret key\. . .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)\. . .SH "AUTHOR" Frank Denis (github [at] pureftpd [dot] org) minisign-0.12/src/000077500000000000000000000000001474202631600140455ustar00rootroot00000000000000minisign-0.12/src/base64.c000066400000000000000000000144041474202631600153000ustar00rootroot00000000000000 #include #include #include "base64.h" unsigned char * b64_to_bin(unsigned char *const bin, const char *b64, size_t bin_maxlen, size_t b64_len, size_t *const bin_len_p) { #define REV64_EOT 128U #define REV64_NONE 64U #define REV64_PAD '=' static const unsigned char rev64chars[256] = { REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, 62U, REV64_NONE, REV64_NONE, REV64_NONE, 63U, 52U, 53U, 54U, 55U, 56U, 57U, 58U, 59U, 60U, 61U, REV64_NONE, REV64_NONE, REV64_NONE, REV64_EOT, REV64_NONE, REV64_NONE, REV64_NONE, 0U, 1U, 2U, 3U, 4U, 5U, 6U, 7U, 8U, 9U, 10U, 11U, 12U, 13U, 14U, 15U, 16U, 17U, 18U, 19U, 20U, 21U, 22U, 23U, 24U, 25U, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, 26U, 27U, 28U, 29U, 30U, 31U, 32U, 33U, 34U, 35U, 36U, 37U, 38U, 39U, 40U, 41U, 42U, 43U, 44U, 45U, 46U, 47U, 48U, 49U, 50U, 51U, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE, REV64_NONE }; const unsigned char *b64_u = (const unsigned char *) b64; unsigned char *bin_w = bin; unsigned char mask = 0U; unsigned char t0 = 0, t1 = 0, t2 = 0, t3 = 0; uint32_t t = 0; size_t i; if (b64_len % 4U != 0U || (i = b64_len / 4U) <= 0U || bin_maxlen < i * 3U - (b64_u[b64_len - 1U] == REV64_PAD) - (b64_u[b64_len - 2U] == REV64_PAD)) { return NULL; } while (i-- > 0U) { t0 = rev64chars[*b64_u++]; t1 = rev64chars[*b64_u++]; t2 = rev64chars[*b64_u++]; t3 = rev64chars[*b64_u++]; t = t3 | ((uint32_t) t2 << 6) | ((uint32_t) t1 << 12) | ((uint32_t) t0 << 18); mask = t0 | t1 | t2 | t3; if ((mask & (REV64_NONE | REV64_EOT)) != 0U) { if ((mask & REV64_NONE) != 0U || i > 0U) { return NULL; } break; } *bin_w++ = (unsigned char) (t >> 16); *bin_w++ = (unsigned char) (t >> 8); *bin_w++ = (unsigned char) t; } if ((mask & REV64_EOT) != 0U) { if (((t0 | t1) & REV64_EOT) != 0U || t3 != REV64_EOT) { return NULL; } *bin_w++ = (unsigned char) (t >> 16); if (t2 != REV64_EOT) { *bin_w++ = (unsigned char) (t >> 8); } } if (bin_len_p != NULL) { *bin_len_p = (size_t) (bin_w - bin); } return bin; } char * bin_to_b64(char *const b64, const unsigned char *bin, size_t b64_maxlen, size_t bin_len) { #define B64_PAD '=' static const char b64chars[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; char *b64_w = b64; if (b64_maxlen < (((bin_len + 2U) / 3U) * 4U + 1U)) { return NULL; } while (bin_len > (size_t) 2U) { const unsigned char t0 = (unsigned char) *bin++; const unsigned char t1 = (unsigned char) *bin++; const unsigned char t2 = (unsigned char) *bin++; *b64_w++ = b64chars[(t0 >> 2) & 63]; *b64_w++ = b64chars[((t0 << 4) & 48) | ((t1 >> 4) & 15)]; *b64_w++ = b64chars[((t1 << 2) & 60) | ((t2 >> 6) & 3)]; *b64_w++ = b64chars[t2 & 63]; bin_len -= (size_t) 3U; } if (bin_len > (size_t) 0U) { const unsigned char t0 = (unsigned char) bin[0]; *b64_w++ = b64chars[(t0 >> 2) & 63]; if (bin_len == 1U) { *b64_w++ = b64chars[((t0 << 4) & 48)]; *b64_w++ = B64_PAD; } else { const unsigned char t1 = (unsigned char) bin[1]; *b64_w++ = b64chars[((t0 << 4) & 48) | ((t1 >> 4) & 15)]; *b64_w++ = b64chars[((t1 << 2) & 60)]; } *b64_w++ = B64_PAD; } *b64_w = 0; return b64; } minisign-0.12/src/base64.h000066400000000000000000000006551474202631600153100ustar00rootroot00000000000000 #ifndef BASE64_H #define BASE64_H #include unsigned char *b64_to_bin(unsigned char *const bin, const char *b64, size_t bin_maxlen, size_t b64_len, size_t *const bin_len_p); char *bin_to_b64(char *const b64, const unsigned char *bin, size_t b64_maxlen, size_t bin_len); #define B64_MAX_LEN_FROM_BIN_LEN(X) (((X) + 2) / 3 * 4 + 1) #define BIN_MAX_LEN_FROM_B64_LEN(X) ((X) / 4 * 3) #endif minisign-0.12/src/get_line.c000066400000000000000000000047031474202631600160030ustar00rootroot00000000000000 #if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__)) || defined(__HAIKU__) # include #endif #include #include #include #include #include #if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__)) || defined(__HAIKU__) # include # include # include # include #elif defined(_WIN32) # include #endif #include "get_line.h" #include "helpers.h" #ifndef TCSAFLUSH # define TCSAFLUSH 0 #endif #ifndef VERIFY_ONLY static void disable_echo(void) { fflush(stdout); fflush(stderr); # if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__)) || defined(__HAIKU__) { struct termios p; if (!isatty(0) || tcgetattr(0, &p) != 0) { return; } p.c_lflag &= ~ECHO; tcsetattr(0, TCSAFLUSH, &p); } # elif defined(_WIN32) { HANDLE handle = GetStdHandle(STD_INPUT_HANDLE); DWORD mode = 0; GetConsoleMode(handle, &mode); SetConsoleMode(handle, mode & ~ENABLE_ECHO_INPUT); } # endif } static void enable_echo(void) { fflush(stdout); fflush(stderr); # if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__)) || defined(__HAIKU__) { struct termios p; if (!isatty(0) || tcgetattr(0, &p) != 0) { return; } p.c_lflag |= ECHO; tcsetattr(0, TCSAFLUSH, &p); } # elif defined(_WIN32) { HANDLE handle = GetStdHandle(STD_INPUT_HANDLE); DWORD mode = 0; GetConsoleMode(handle, &mode); SetConsoleMode(handle, mode | ENABLE_ECHO_INPUT); } # endif } int get_line(char *line, size_t max_len, const char *prompt) { memset(line, 0, max_len); if (max_len < 2U || max_len > INT_MAX) { return -1; } xfprintf(stderr, "%s", prompt); fflush(stderr); if (fgets(line, (int) max_len, stdin) == NULL) { return -1; } trim(line); if (strlen(line) >= max_len) { fprintf(stderr, "(truncated to %u characters)\n", (unsigned int) max_len); } else if (*line == 0) { fprintf(stderr, "(empty)\n"); } else { fprintf(stderr, "\n"); } return 0; } int get_password(char *pwd, size_t max_len, const char *prompt) { int ret; disable_echo(); ret = get_line(pwd, max_len, prompt); enable_echo(); return ret; } #endif minisign-0.12/src/get_line.h000066400000000000000000000002571474202631600160100ustar00rootroot00000000000000 #ifndef GET_LINE_H #define GET_LINE_H int get_line(char *line, size_t max_len, const char *prompt); int get_password(char *pwd, size_t max_len, const char *prompt); #endif minisign-0.12/src/helpers.c000066400000000000000000000114561474202631600156620ustar00rootroot00000000000000 #if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__)) || defined(__HAIKU__) # include # include # include #elif defined(_WIN32) # include #endif #include #include #include #include #include #include #ifdef LIBZODIUM # include "zodium.h" #else # include #endif #include "base64.h" #include "helpers.h" uint64_t le64_load(const unsigned char *p) { return ((uint64_t) (p[0])) | ((uint64_t) (p[1]) << 8) | ((uint64_t) (p[2]) << 16) | ((uint64_t) (p[3]) << 24) | ((uint64_t) (p[4]) << 32) | ((uint64_t) (p[5]) << 40) | ((uint64_t) (p[6]) << 48) | ((uint64_t) (p[7]) << 56); } void le64_store(unsigned char *p, uint64_t x) { p[0] = (unsigned char) x; p[1] = (unsigned char) (x >> 8); p[2] = (unsigned char) (x >> 16); p[3] = (unsigned char) (x >> 24); p[4] = (unsigned char) (x >> 32); p[5] = (unsigned char) (x >> 40); p[6] = (unsigned char) (x >> 48); p[7] = (unsigned char) (x >> 56); } void exit_err(const char *msg) { perror(msg == NULL ? "" : msg); exit(2); } void exit_msg(const char *msg) { fprintf(stderr, "%s\n", msg); exit(2); } void * xmalloc(size_t size) { void *pnt; if ((pnt = malloc(size)) == NULL) { exit_err("malloc()"); } return pnt; } char * xstrdup(const char *str) { char *clone; if ((clone = strdup(str)) == NULL) { exit_err("strdup()"); } return clone; } void * xsodium_malloc(size_t size) { void *pnt; if ((pnt = sodium_malloc(size)) == NULL) { exit_err("sodium_malloc()"); } return pnt; } void xor_buf(unsigned char *dst, const unsigned char *src, size_t len) { size_t i; for (i = (size_t) 0U; i < len; i++) { dst[i] ^= src[i]; } } int xfprintf(FILE *fp, const char *format, ...) { char *out; size_t out_maxlen = 4096U; int len; va_list va; va_start(va, format); out = xsodium_malloc(out_maxlen); len = vsnprintf(out, out_maxlen, format, va); if (len < 0 || len >= (int) out_maxlen) { va_end(va); exit_msg("xfprintf() overflow"); } va_end(va); if (fwrite(out, (size_t) len, 1U, fp) != 1U) { sodium_free(out); exit_err("fwrite()"); } sodium_free(out); return 0; } 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; b64 = xsodium_malloc(b64_maxlen); if (bin_to_b64(b64, bin, b64_maxlen, bin_len) == NULL) { sodium_free(b64); abort(); } xfprintf(fp, "%s\n", b64); sodium_free(b64); return 0; } int xfclose(FILE *fp) { if (fp == NULL) { abort(); } if (fclose(fp) != 0) { exit_err("fclose()"); } return 0; } int trim(char *str) { size_t i = strlen(str); int t = 0; while (i-- > (size_t) 0U) { if (str[i] == '\n') { str[i] = 0; t = 1; } else if (str[i] == '\r') { str[i] = 0; } } return t; } const char * file_basename(const char *file) { char *ptr; if ((ptr = strrchr(file, DIR_SEP)) != NULL) { return ptr + 1; } return file; } FILE * fopen_create_useronly(const char *file) { #if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__)) || defined(__HAIKU__) int fd; if ((fd = open(file, O_CREAT | O_TRUNC | O_WRONLY, (mode_t) 0600)) == -1) { return NULL; } return fdopen(fd, "w"); #else return fopen(file, "w"); #endif } int basedir_create_useronly(const char *file) { const char *basename; char *dir; int ret = -1; dir = xstrdup(file); basename = file_basename(dir); if (basename == dir) { free(dir); return 0; } dir[basename - dir - 1] = 0; #if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__)) || defined(__HAIKU__) if (*dir == 0 || mkdir(dir, 0700) == 0 || errno == EEXIST) { ret = 0; } #elif defined(_WIN32) if (*dir == 0 || _mkdir(dir) == 0 || errno == EEXIST) { ret = 0; } #endif free(dir); return ret; } char * get_home_dir(void) { char *dir; #ifdef _WIN32 const char *hd; const char *hp; #endif if ((dir = getenv("HOME")) != NULL) { return xstrdup(dir); } #ifdef _WIN32 if ((dir = getenv("USERPROFILE")) != NULL) { return xstrdup(dir); } if ((dir = getenv("USERPROFILE")) != NULL) { return xstrdup(dir); } if ((hd = getenv("HOMEDRIVE")) != NULL && (hp = getenv("HOMEPATH")) != NULL) { if (asprintf(&dir, "%s%s", hd, hp) < 0) { exit_err("asprintf()"); } return dir; } #endif return NULL; } minisign-0.12/src/helpers.h000066400000000000000000000017531474202631600156660ustar00rootroot00000000000000 #ifndef HELPERS_H #define HELPERS_H 1 #include #include #if !defined(__GNUC__) && !defined(__attribute__) # define __attribute__(X) #endif #ifdef _WIN32 # define DIR_SEP '\\' #else # define DIR_SEP '/' #endif uint64_t le64_load(const unsigned char *p); void le64_store(unsigned char *p, uint64_t x); void exit_err(const char *msg) __attribute__((noreturn)); void exit_msg(const char *msg) __attribute__((noreturn)); void *xmalloc(size_t size); char *xstrdup(const char *str); void *xsodium_malloc(size_t size); void xor_buf(unsigned char *dst, const unsigned char *src, size_t len); int xfput_b64(FILE *fp, const unsigned char *bin, size_t bin_len); int xfprintf(FILE *fp, const char *format, ...) __attribute__((format(printf, 2, 3))); int xfclose(FILE *fp); int trim(char *str); const char *file_basename(const char *file); FILE *fopen_create_useronly(const char *file); int basedir_create_useronly(const char *file); char *get_home_dir(void); #endif minisign-0.12/src/libzodium.zig000066400000000000000000000061471474202631600165660ustar00rootroot00000000000000const 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; } minisign-0.12/src/manpage.md000066400000000000000000000064631474202631600160100ustar00rootroot00000000000000 minisign(1) -- A dead simple tool to sign files and verify signatures. ====================================================================== ## SYNOPSIS `minisign` -G [-p pubkey_file] [-s seckey_file] [-W] `minisign` -R [-s seckey_file] [-p pubkey_file] `minisign` -C [-s seckey_file] [-W] `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 **Minisign** is a dead simple tool to sign files and verify signatures. It is portable, lightweight, and uses the highly secure [Ed25519](http://ed25519.cr.yp.to/) public-key signature system. ## OPTIONS These options control the actions of `minisign`. * `-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 to sign/verify * `-o`: Combined with -V, output the file content after verification * `-p `: Public key file (default: ./minisign.pub) * `-P `: Public key, as a base64 string * `-s `: Secret key file (default: ~/.minisign/minisign.key) * `-W`: Do not encrypt/decrypt the secret key with a password * `-x `: Signature file (default: <file>.minisig) * `-c `: Add a one-line untrusted comment * `-t `: 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 Creating a key pair `minisign` -G The public key is printed and put into the `minisign.pub` file. The secret key is encrypted and saved as a file named `~/.minisign/minisign.key`. Signing files $ `minisign` -Sm myfile.txt $ `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: $ `minisign` -Sm myfile.txt -t 'This comment will be signed as well' The secret key is loaded from `${MINISIGN_CONFIG_DIR}/minisign.key`, `~/.minisign/minisign.key`, or its path can be explicitly set with the `-s ` command-line switch. Verifying a file $ `minisign` -Vm myfile.txt -P <pubkey> or $ `minisign` -Vm myfile.txt -p signature.pub This requires the signature `myfile.txt.minisig` to be present in the same directory. The public key can either reside in a file (`./minisign.pub` by default) or be directly specified on the command line. ## Notes 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. Trusted comments can be used to add instructions or application-specific metadata (intended file name, timestamps, resource identifiers, version numbers to prevent downgrade attacks). ## AUTHOR Frank Denis (github [at] pureftpd [dot] org) minisign-0.12/src/minisign.c000066400000000000000000001040361474202631600160320ustar00rootroot00000000000000 #include #include #include #include #include #include #include #include #include #include #ifdef LIBZODIUM # include "zodium.h" #else # include #endif #include "base64.h" #include "get_line.h" #include "helpers.h" #include "minisign.h" #ifndef VERIFY_ONLY static const char *getopt_options = "CGSVRHhc:flm:oP:p:qQs:t:vWx:"; #else static const char *getopt_options = "VhHm:oP:p:qQvx:"; #endif static void usage(void) __attribute__((noreturn)); static void usage(void) { puts( "Usage:\n" #ifndef VERIFY_ONLY "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" "-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 to sign/verify\n" "-o combined with -V, output the file content after verification\n" "-p public key file (default: ./minisign.pub)\n" "-P public key, as a base64 string\n" #ifndef VERIFY_ONLY "-s secret key file (default: ~/.minisign/minisign.key)\n" "-W do not encrypt/decrypt the secret key with a password\n" #endif "-x signature file (default: .minisig)\n" #ifndef VERIFY_ONLY "-c add a one-line untrusted comment\n" "-t add a one-line trusted comment\n" #endif "-q quiet mode, suppress output\n" "-Q pretty quiet mode, only print the trusted comment\n" "-f force. Combined with -G, overwrite a previous key pair\n" "-v display version number\n"); exit(2); } static unsigned char * message_load_hashed(size_t *message_len, const char *message_file) { crypto_generichash_state hs; unsigned char buf[65536U]; unsigned char *message; FILE *fp; size_t n; if ((fp = fopen(message_file, "rb")) == NULL) { exit_err(message_file); } crypto_generichash_init(&hs, NULL, 0U, crypto_generichash_BYTES_MAX); while ((n = fread(buf, 1U, sizeof buf, fp)) > 0U) { crypto_generichash_update(&hs, buf, n); } if (!feof(fp)) { exit_err(message_file); } xfclose(fp); message = xmalloc(crypto_generichash_BYTES_MAX); crypto_generichash_final(&hs, message, crypto_generichash_BYTES_MAX); *message_len = crypto_generichash_BYTES_MAX; return message; } static unsigned char * message_load(size_t *message_len, const char *message_file, int hashed) { FILE *fp; unsigned char *message; off_t message_len_; if (hashed != 0) { return message_load_hashed(message_len, message_file); } if ((fp = fopen(message_file, "rb")) == NULL || fseeko(fp, 0, SEEK_END) != 0 || (message_len_ = ftello(fp)) == (off_t) -1) { exit_err(message_file); } assert(hashed == 0); if ((uintmax_t) message_len_ > (uintmax_t) SIZE_MAX || message_len_ < (off_t) 0) { abort(); } message = xmalloc((*message_len = (size_t) message_len_)); rewind(fp); if (*message_len > 0U && fread(message, *message_len, (size_t) 1U, fp) != 1U) { exit_msg("Error while loading the message"); } xfclose(fp); return message; } static int output_file(const char *message_file) { unsigned char buf[65536U]; FILE *fp; size_t n; if ((fp = fopen(message_file, "rb")) == NULL) { exit_err(message_file); } while ((n = fread(buf, 1U, sizeof buf, fp)) > 0U) { if (fwrite(buf, 1U, n, stdout) != n) { exit_err(message_file); } } if (!feof(fp) || fflush(stdout) != 0) { exit_err(message_file); } xfclose(fp); return 0; } static SigStruct * sig_load(const char *sig_file, unsigned char global_sig[crypto_sign_BYTES], int *hashed, char trusted_comment[TRUSTEDCOMMENTMAXBYTES], size_t trusted_comment_maxlen) { char comment[COMMENTMAXBYTES]; SigStruct *sig_struct; 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; size_t sig_struct_len; if ((fp = fopen(sig_file, "r")) == NULL) { exit_err(sig_file); } 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 " "\"" COMMENT_PREFIX "\""); } sig_s_size = B64_MAX_LEN_FROM_BIN_LEN(sizeof *sig_struct) + 2U; sig_s = xmalloc(sig_s_size); if (fgets(sig_s, (int) sig_s_size, fp) == NULL) { exit_msg("Error while reading the signature file"); } 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"); } if (strncmp(trusted_comment, TRUSTED_COMMENT_PREFIX, (sizeof TRUSTED_COMMENT_PREFIX) - 1U) != 0) { exit_msg( "Trusted signature comment should start with " "\"" TRUSTED_COMMENT_PREFIX "\""); } memmove(trusted_comment, trusted_comment + sizeof TRUSTED_COMMENT_PREFIX - 1U, strlen(trusted_comment + sizeof TRUSTED_COMMENT_PREFIX - 1U) + 1U); 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) { exit_msg("Error while reading the signature file"); } trim(global_sig_s); xfclose(fp); sig_struct = xmalloc(sizeof *sig_struct); if (b64_to_bin((unsigned char *) (void *) sig_struct, sig_s, sizeof *sig_struct, strlen(sig_s), &sig_struct_len) == NULL || sig_struct_len != sizeof *sig_struct) { exit_msg("base64 conversion failed - was an actual signature given?"); } free(sig_s); if (memcmp(sig_struct->sig_alg, SIGALG, sizeof sig_struct->sig_alg) == 0) { *hashed = 0; } else if (memcmp(sig_struct->sig_alg, SIGALG_HASHED, sizeof sig_struct->sig_alg) == 0) { *hashed = 1; } else { exit_msg("Unsupported signature algorithm"); } if (b64_to_bin(global_sig, global_sig_s, crypto_sign_BYTES, strlen(global_sig_s), &global_sig_len) == NULL || global_sig_len != crypto_sign_BYTES) { exit_msg("base64 conversion failed - was an actual signature given?"); } free(global_sig_s); return sig_struct; } static PubkeyStruct * pubkey_load_string(const char *pubkey_s) { PubkeyStruct *pubkey_struct; size_t pubkey_struct_len; pubkey_struct = xsodium_malloc(sizeof *pubkey_struct); if (b64_to_bin((unsigned char *) (void *) pubkey_struct, pubkey_s, sizeof *pubkey_struct, strlen(pubkey_s), &pubkey_struct_len) == NULL || pubkey_struct_len != sizeof *pubkey_struct) { exit_msg("base64 conversion failed - was an actual public key given?"); } if (memcmp(pubkey_struct->sig_alg, SIGALG, sizeof pubkey_struct->sig_alg) != 0) { exit_msg("Unsupported signature algorithm"); } return pubkey_struct; } static PubkeyStruct * pubkey_load_file(const char *pk_file) { char pk_comment[COMMENTMAXBYTES]; PubkeyStruct *pubkey_struct; FILE *fp; char *pubkey_s = NULL; size_t pubkey_s_size; if ((fp = fopen(pk_file, "r")) == NULL) { exit_err(pk_file); } if (fgets(pk_comment, (int) sizeof pk_comment, fp) == NULL) { exit_msg("Error while loading the public key file"); } pubkey_s_size = B64_MAX_LEN_FROM_BIN_LEN(sizeof *pubkey_struct) + 2U; pubkey_s = xmalloc(pubkey_s_size); if (fgets(pubkey_s, (int) pubkey_s_size, fp) == NULL) { exit_msg("Error while loading the public key file"); } trim(pubkey_s); xfclose(fp); pubkey_struct = pubkey_load_string(pubkey_s); free(pubkey_s); return pubkey_struct; } static PubkeyStruct * pubkey_load(const char *pk_file, const char *pubkey_s) { if (pk_file != NULL && pubkey_s != NULL) { exit_msg("A public key cannot be provided both inline and as a file"); } if (pubkey_s != NULL) { return pubkey_load_string(pubkey_s); } else if (pk_file != NULL) { return pubkey_load_file(pk_file); } exit_msg("A public key is required"); } #ifndef VERIFY_ONLY static void seckey_compute_chk(unsigned char chk[crypto_generichash_BYTES], const SeckeyStruct *seckey_struct) { crypto_generichash_state hs; crypto_generichash_init(&hs, NULL, 0U, sizeof seckey_struct->keynum_sk.chk); crypto_generichash_update(&hs, seckey_struct->sig_alg, sizeof seckey_struct->sig_alg); crypto_generichash_update(&hs, seckey_struct->keynum_sk.keynum, sizeof seckey_struct->keynum_sk.keynum); crypto_generichash_update(&hs, seckey_struct->keynum_sk.sk, sizeof seckey_struct->keynum_sk.sk); crypto_generichash_final(&hs, chk, sizeof seckey_struct->keynum_sk.chk); } static void decrypt_key(SeckeyStruct *const seckey_struct, unsigned char chk[crypto_generichash_BYTES]) { char *pwd = xsodium_malloc(PASSWORDMAXBYTES); unsigned char *stream; if (get_password(pwd, PASSWORDMAXBYTES, "Password: ") != 0) { exit_msg("get_password()"); } printf("Deriving a key from the password and decrypting the secret key... "); fflush(stdout); stream = xsodium_malloc(sizeof seckey_struct->keynum_sk); if (crypto_pwhash_scryptsalsa208sha256(stream, sizeof seckey_struct->keynum_sk, pwd, strlen(pwd), seckey_struct->kdf_salt, le64_load(seckey_struct->kdf_opslimit_le), le64_load(seckey_struct->kdf_memlimit_le)) != 0) { exit_err("Unable to complete key derivation - This probably means out of memory"); } sodium_free(pwd); xor_buf((unsigned char *) (void *) &seckey_struct->keynum_sk, stream, sizeof seckey_struct->keynum_sk); sodium_free(stream); puts("done\n"); 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, 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 static int verify(PubkeyStruct *pubkey_struct, const char *message_file, const char *sig_file, int quiet, int output, int allow_legacy) { char trusted_comment[TRUSTEDCOMMENTMAXBYTES]; unsigned char global_sig[crypto_sign_BYTES]; FILE *info_fp = stdout; unsigned char *sig_and_trusted_comment; SigStruct *sig_struct; unsigned char *message; size_t message_len; size_t trusted_comment_len; int hashed; if (output != 0) { info_fp = stderr; } sig_struct = sig_load(sig_file, global_sig, &hashed, trusted_comment, sizeof trusted_comment); if (hashed == 0 && allow_legacy == 0) { if (quiet == 0) { fprintf(stderr, "Legacy (non-prehashed) signature found\n"); } exit(1); } message = message_load(&message_len, message_file, hashed); if (memcmp(sig_struct->keynum, pubkey_struct->keynum_pk.keynum, sizeof sig_struct->keynum) != 0) { fprintf(stderr, "Signature key id in %s is %016" 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); } if (crypto_sign_verify_detached(sig_struct->sig, message, message_len, pubkey_struct->keynum_pk.pk) != 0) { if (quiet == 0) { fprintf(stderr, "Signature verification failed\n"); } exit(1); } free(message); trusted_comment_len = strlen(trusted_comment); sig_and_trusted_comment = xmalloc((sizeof sig_struct->sig) + trusted_comment_len); memcpy(sig_and_trusted_comment, sig_struct->sig, sizeof sig_struct->sig); memcpy(sig_and_trusted_comment + sizeof sig_struct->sig, trusted_comment, trusted_comment_len); if (crypto_sign_verify_detached(global_sig, sig_and_trusted_comment, (sizeof sig_struct->sig) + trusted_comment_len, pubkey_struct->keynum_pk.pk) != 0) { if (quiet == 0) { fprintf(stderr, "Comment signature verification failed\n"); } exit(1); } sodium_free(pubkey_struct); free(sig_and_trusted_comment); free(sig_struct); if (quiet == 0) { fprintf(info_fp, "Signature and comment signature verified\n" "Trusted comment: %s\n", trusted_comment); } else if (quiet == 2) { fprintf(info_fp, "%s\n", trusted_comment); } if (output != 0 && output_file(message_file) != 0) { exit(2); } return 0; } static char * append_sig_suffix(const char *message_file) { char *sig_file; size_t message_file_len = strlen(message_file); sig_file = xmalloc(message_file_len + sizeof SIG_SUFFIX); memcpy(sig_file, message_file, message_file_len); memcpy(sig_file + message_file_len, SIG_SUFFIX, sizeof SIG_SUFFIX); return sig_file; } #ifndef VERIFY_ONLY static char * default_trusted_comment(const char *message_file, int hashed) { char *ret; time_t ts = time(NULL); if (asprintf(&ret, "timestamp:%lu\tfile:%s%s", (unsigned long) ts, file_basename(message_file), hashed == 0 ? "" : "\thashed") < 0 || ret == NULL) { exit_err("asprintf()"); } return ret; } static void sign(SeckeyStruct *seckey_struct, PubkeyStruct *pubkey_struct, const char *message_file, const char *sig_file, const char *comment, const char *trusted_comment, int legacy) { unsigned char global_sig[crypto_sign_BYTES]; SigStruct sig_struct; FILE *fp; unsigned char *message; unsigned char *sig_and_trusted_comment; char *tmp_trusted_comment = NULL; size_t comment_len; size_t trusted_comment_len; size_t message_len; int hashed = 1; if (legacy != 0) { hashed = 0; } if (trusted_comment == NULL || *trusted_comment == 0) { tmp_trusted_comment = default_trusted_comment(message_file, hashed); trusted_comment = tmp_trusted_comment; } message = message_load(&message_len, message_file, hashed); memcpy(sig_struct.sig_alg, hashed ? SIGALG_HASHED : SIGALG, sizeof sig_struct.sig_alg); memcpy(sig_struct.keynum, seckey_struct->keynum_sk.keynum, sizeof sig_struct.keynum); crypto_sign_detached(sig_struct.sig, NULL, message, message_len, seckey_struct->keynum_sk.sk); free(message); if ((fp = fopen(sig_file, "w")) == NULL) { exit_err(sig_file); } comment_len = strlen(comment); assert(strrchr(comment, '\r') == NULL && strrchr(comment, '\n') == NULL); assert(COMMENTMAXBYTES > sizeof COMMENT_PREFIX); if (comment_len >= COMMENTMAXBYTES - sizeof COMMENT_PREFIX) { fprintf(stderr, "Warning: comment too long. " "This breaks compatibility with signify.\n"); } xfprintf(fp, "%s%s\n", COMMENT_PREFIX, comment); xfput_b64(fp, (unsigned char *) (void *) &sig_struct, sizeof sig_struct); xfprintf(fp, "%s%s\n", TRUSTED_COMMENT_PREFIX, trusted_comment); trusted_comment_len = strlen(trusted_comment); assert(strrchr(trusted_comment, '\r') == NULL && strrchr(trusted_comment, '\n') == NULL); if (trusted_comment_len >= TRUSTEDCOMMENTMAXBYTES - sizeof TRUSTED_COMMENT_PREFIX) { exit_msg("Trusted comment too long"); } sig_and_trusted_comment = xmalloc((sizeof sig_struct.sig) + trusted_comment_len); memcpy(sig_and_trusted_comment, sig_struct.sig, sizeof sig_struct.sig); memcpy(sig_and_trusted_comment + sizeof sig_struct.sig, trusted_comment, trusted_comment_len); if (crypto_sign_detached(global_sig, NULL, sig_and_trusted_comment, (sizeof sig_struct.sig) + trusted_comment_len, seckey_struct->keynum_sk.sk) != 0) { exit_msg("Unable to compute a signature"); } if (pubkey_struct != NULL && (memcmp(pubkey_struct->keynum_pk.keynum, seckey_struct->keynum_sk.keynum, KEYNUMBYTES) != 0 || crypto_sign_verify_detached(global_sig, sig_and_trusted_comment, (sizeof sig_struct.sig) + trusted_comment_len, pubkey_struct->keynum_pk.pk) != 0)) { exit_msg("Verification would fail with the given public key"); } xfput_b64(fp, (unsigned char *) (void *) &global_sig, sizeof global_sig); xfclose(fp); free(sig_and_trusted_comment); free(tmp_trusted_comment); } static int sign_all(SeckeyStruct *seckey_struct, PubkeyStruct *pubkey_struct, const char *message_file, const char *additional_files[], int additional_count, const char *sig_file, const char *comment, const char *trusted_comment, int legacy) { char *additional_sig_file; int i; sign(seckey_struct, pubkey_struct, message_file, sig_file, comment, trusted_comment, legacy); for (i = 0; i < additional_count; i++) { additional_sig_file = append_sig_suffix(additional_files[i]); sign(seckey_struct, pubkey_struct, additional_files[i], additional_sig_file, comment, trusted_comment, legacy); free(additional_sig_file); } sodium_free(seckey_struct); sodium_free(pubkey_struct); return 0; } static void abort_on_existing_key_file(const char *file) { FILE *fp; int exists = 0; if ((fp = fopen(file, "r")) != NULL) { exists = 1; fclose(fp); } if (exists != 0) { fprintf(stderr, "Key generation aborted:\n" "%s already exists.\n\n" "If you really want to overwrite the existing key pair, add the -f switch to \n" "force this operation.\n", file); exit(1); } } static void abort_on_existing_key_files(const char *pk_file, const char *sk_file, int force) { if (force == 0) { abort_on_existing_key_file(pk_file); abort_on_existing_key_file(sk_file); } } static void write_pk_file(const char *pk_file, const PubkeyStruct *pubkey_struct) { FILE *fp; if ((fp = fopen(pk_file, "w")) == NULL) { exit_err(pk_file); } 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, int unencrypted_key) { 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, 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); if (unencrypted_key == 0) { encrypt_key(seckey_struct); } 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"); } if ((fp = fopen_create_useronly(sk_file)) == NULL) { exit_err(sk_file); } xfprintf(fp, "%s%s\n", COMMENT_PREFIX, comment); xfput_b64(fp, (unsigned char *) (void *) seckey_struct, sizeof *seckey_struct); xfclose(fp); sodium_free(seckey_struct); write_pk_file(pk_file, pubkey_struct); printf("The secret key was saved as %s - Keep it secret!\n", sk_file); printf("The public key was saved as %s - That one can be public.\n\n", pk_file); puts("Files signed using this key pair can be verified with the following command:\n"); printf("minisign -Vm -P "); xfput_b64(stdout, (unsigned char *) (void *) pubkey_struct, sizeof *pubkey_struct); puts(""); sodium_free(pubkey_struct); return 0; } static int recreate_pk(const char *pk_file, const char *sk_file, int force) { SeckeyStruct *seckey_struct; PubkeyStruct pubkey_struct; if (force == 0) { abort_on_existing_key_file(pk_file); } 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); memcpy(pubkey_struct.keynum_pk.keynum, seckey_struct->keynum_sk.keynum, sizeof pubkey_struct.keynum_pk.keynum); assert(sizeof seckey_struct->keynum_sk.sk > crypto_sign_PUBLICKEYBYTES); memcpy(pubkey_struct.keynum_pk.pk, seckey_struct->keynum_sk.sk + (sizeof seckey_struct->keynum_sk.sk) - crypto_sign_PUBLICKEYBYTES, sizeof pubkey_struct.keynum_pk.pk); sodium_free(seckey_struct); write_pk_file(pk_file, &pubkey_struct); 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 static char * sig_config_dir(void) { const char *config_dir_env; char *config_dir; char *home_dir; config_dir = NULL; if ((config_dir_env = getenv(SIG_DEFAULT_CONFIG_DIR_ENV_VAR)) != NULL) { config_dir = xstrdup(config_dir_env); } else if ((home_dir = get_home_dir()) != NULL) { if (asprintf(&config_dir, "%s%c%s", home_dir, DIR_SEP, SIG_DEFAULT_CONFIG_DIR) < 0 || config_dir == NULL) { exit_err("asprintf()"); } free(home_dir); } return config_dir; } static char * sig_default_skfile(void) { char *config_dir; char *skfile; if ((config_dir = sig_config_dir()) == NULL) { skfile = xstrdup(SIG_DEFAULT_SKFILE); return skfile; } if (asprintf(&skfile, "%s%c%s", config_dir, DIR_SEP, SIG_DEFAULT_SKFILE) < 0 || skfile == NULL) { exit_err("asprintf()"); } free(config_dir); return skfile; } #endif int main(int argc, char **argv) { const char *pk_file = NULL; #ifndef VERIFY_ONLY char *sk_file = sig_default_skfile(); #endif 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; #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) { #ifndef VERIFY_ONLY case 'G': if (action != ACTION_NONE && action != ACTION_GENERATE) { usage(); } action = ACTION_GENERATE; break; case 'S': if (action != ACTION_NONE && action != ACTION_SIGN) { usage(); } 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(); } action = ACTION_RECREATE_PK; break; #endif case 'V': if (action != ACTION_NONE && action != ACTION_VERIFY) { usage(); } action = ACTION_VERIFY; break; #ifndef VERIFY_ONLY case 'c': comment = optarg; break; case 'f': force = 1; break; #endif case 'h': usage(); case 'H': allow_legacy = 0; break; #ifndef VERIFY_ONLY case 'l': sign_legacy = 1; break; #endif case 'm': message_file = optarg; break; case 'o': output = 1; break; case 'p': pk_file = optarg; break; case 'P': pubkey_s = optarg; break; case 'q': quiet = 1; break; case 'Q': quiet = 2; break; #ifndef VERIFY_ONLY case 's': free(sk_file); sk_file = xstrdup(optarg); break; case 't': trusted_comment = optarg; break; case 'W': unencrypted_key = 1; break; #endif case 'x': sig_file = optarg; break; case 'v': puts(VERSION_STRING); return 0; case '?': usage(); } if (opt_flag > 0 && opt_flag <= (int) sizeof opt_seen / 8) { if ((opt_seen[opt_flag / 8] & (1U << (opt_flag & 7))) != 0) { fprintf(stderr, "Duplicate option: -- %c\n\n", opt_flag); usage(); } opt_seen[opt_flag / 8] |= 1U << (opt_flag & 7); } } if (sodium_init() != 0) { fprintf(stderr, "Unable to initialize the Sodium library\n"); return 2; } switch (action) { #ifndef VERIFY_ONLY case ACTION_GENERATE: if (comment == NULL || *comment == 0) { comment = SECRETKEY_DEFAULT_COMMENT; } if (pk_file == NULL) { pk_file = SIG_DEFAULT_PKFILE; } return generate(pk_file, sk_file, comment, force, unencrypted_key) != 0; case ACTION_SIGN: if (message_file == NULL) { usage(); } if (sig_file == NULL || *sig_file == 0) { sig_file = append_sig_suffix(message_file); } if (comment == NULL || *comment == 0) { comment = DEFAULT_COMMENT; } return sign_all( 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; case ACTION_RECREATE_PK: if (pk_file == NULL) { 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) { usage(); } if (sig_file == NULL || *sig_file == 0) { sig_file = append_sig_suffix(message_file); } if (pk_file == NULL && pubkey_s == NULL) { pk_file = SIG_DEFAULT_PKFILE; } return verify(pubkey_load(pk_file, pubkey_s), message_file, sig_file, quiet, output, allow_legacy); default: usage(); } return 0; } minisign-0.12/src/minisign.h000066400000000000000000000040401474202631600160310ustar00rootroot00000000000000 #ifndef MINISIGN_H #define MINISIGN_H 1 #define COMMENTMAXBYTES 1024 #define KEYNUMBYTES 8 #define PASSWORDMAXBYTES 1024 #define TRUSTEDCOMMENTMAXBYTES 8192 #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" #define SECRETKEY_DEFAULT_COMMENT "minisign encrypted secret key" #define TRUSTED_COMMENT_PREFIX "trusted comment: " #define SIG_DEFAULT_CONFIG_DIR ".minisign" #define SIG_DEFAULT_CONFIG_DIR_ENV_VAR "MINISIGN_CONFIG_DIR" #define SIG_DEFAULT_PKFILE "minisign.pub" #define SIG_DEFAULT_SKFILE "minisign.key" #define SIG_SUFFIX ".minisig" #define VERSION_STRING "minisign 0.12" typedef struct KeynumSK_ { unsigned char keynum[KEYNUMBYTES]; unsigned char sk[crypto_sign_SECRETKEYBYTES]; unsigned char chk[crypto_generichash_BYTES]; } KeynumSK; typedef struct KeynumPK_ { unsigned char keynum[KEYNUMBYTES]; unsigned char pk[crypto_sign_PUBLICKEYBYTES]; } KeynumPK; typedef struct SeckeyStruct_ { unsigned char sig_alg[2]; unsigned char kdf_alg[2]; unsigned char chk_alg[2]; unsigned char kdf_salt[crypto_pwhash_scryptsalsa208sha256_SALTBYTES]; unsigned char kdf_opslimit_le[8]; unsigned char kdf_memlimit_le[8]; KeynumSK keynum_sk; } SeckeyStruct; typedef struct PubkeyStruct_ { unsigned char sig_alg[2]; KeynumPK keynum_pk; } PubkeyStruct; typedef struct SigStruct_ { unsigned char sig_alg[2]; unsigned char keynum[KEYNUMBYTES]; unsigned char sig[crypto_sign_BYTES]; } SigStruct; typedef enum Action_ { ACTION_NONE, ACTION_GENERATE, ACTION_SIGN, ACTION_VERIFY, ACTION_RECREATE_PK, ACTION_UPDATE_PASSWORD } Action; #endif minisign-0.12/src/zodium.h000066400000000000000000000052251474202631600155310ustar00rootroot00000000000000#pragma once #include 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)));