├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── Makefile.am ├── README.md ├── autogen.sh ├── cmake └── config.h.in ├── configure.ac ├── docs ├── Makefile.am ├── asignify.1 └── asignify.pod ├── include ├── Makefile.am └── asignify.h ├── libasignify ├── Makefile.am ├── asignify_internal.h ├── b64_pton.c ├── blake2-impl.h ├── blake2.h ├── blake2b-ref.c ├── chacha.c ├── chacha.h ├── databuf.c ├── encrypt.c ├── generate.c ├── khash.h ├── kvec.h ├── libasignify.ver ├── pbkdf2.c ├── pubkey.c ├── sha2.c ├── sha2.h ├── sign.c ├── signature.c ├── tweetnacl.c ├── tweetnacl.h ├── util.c └── verify.c ├── m4 ├── ax_append_flag.m4 ├── ax_cflags_warn_all.m4 ├── ax_check_openssl.m4 └── ax_require_defined.m4 └── src ├── Makefile.am ├── asignify.c ├── cli.h ├── encrypt.c ├── generate.c ├── readpassphrase_compat.h ├── sign.c └── verify.c /.gitignore: -------------------------------------------------------------------------------- 1 | # Object files 2 | *.o 3 | *.ko 4 | *.obj 5 | *.elf 6 | 7 | # Precompiled Headers 8 | *.gch 9 | *.pch 10 | 11 | # Libraries 12 | *.lib 13 | *.a 14 | *.la 15 | *.lo 16 | 17 | # Shared objects (inc. Windows DLLs) 18 | *.dll 19 | *.so 20 | *.so.* 21 | *.dylib 22 | 23 | # Executables 24 | *.exe 25 | *.out 26 | *.app 27 | *.i*86 28 | *.x86_64 29 | *.hex 30 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.13) 2 | 3 | set(CMAKE_C_STANDARD 11) 4 | 5 | set(LIBASIGNIFY_VERSION_MAJOR 2) 6 | set(LIBASIGNIFY_VERSION_MINOR 0) 7 | set(LIBASIGNIFY_VERSION_PATCH 0) 8 | set(LIBASIGNIFY_VERSION "${LIBASIGNIFY_VERSION_MAJOR}.${LIBASIGNIFY_VERSION_MINOR}.${LIBASIGNIFY_VERSION_PATCH}") 9 | project(asignify LANGUAGES C VERSION "${LIBASIGNIFY_VERSION}") 10 | 11 | OPTION(ENABLE_OPENSSL "Link with openssl [default: ON]" ON) 12 | 13 | include_directories(include) 14 | include_directories(libasignify) 15 | include_directories(src) 16 | include_directories("${CMAKE_BINARY_DIR}") 17 | 18 | include(CheckIncludeFiles) 19 | include(CheckFunctionExists) 20 | include(CheckSymbolExists) 21 | include(CheckCSourceCompiles) 22 | include(CheckCSourceRuns) 23 | include(CheckLibraryExists) 24 | 25 | check_include_files(sys/types.h HAVE_SYS_TYPES_H) 26 | check_include_files(sys/random.h HAVE_SYS_RANDOM_H) 27 | check_include_files(sys/time.h HAVE_SYS_TIME_H) 28 | check_include_files(sys/stat.h HAVE_SYS_STAT_H) 29 | check_include_files(math.h HAVE_MATH_H) 30 | check_include_files(stdio.h HAVE_STDIO_H) 31 | check_include_files(stdlib.h HAVE_STDLIB_H) 32 | check_include_files(stddef.h HAVE_STDDEF_H) 33 | check_include_files(string.h HAVE_STRING_H) 34 | check_include_files(strings.h HAVE_STRINGS_H) 35 | check_include_files(memory.h HAVE_MEMORY_H) 36 | check_include_files(unistd.h HAVE_UNISTD_H) 37 | check_include_files(stdint.h HAVE_STDINT_H) 38 | check_include_files(inttypes.h HAVE_INTTYPES_H) 39 | check_include_files(stdbool.h HAVE_STDBOOL_H) 40 | check_include_files(readpassphrase.h HAVE_READPASSPHRASE_H) 41 | 42 | check_include_files(bsd/libutil.h HAVE_BSD_LIBUTIL_H) 43 | check_include_files(bsd/readpassphrase.h HAVE_BSD_READPASSPHRASE_H) 44 | check_include_files(bsd/err.h HAVE_BSD_ERR_H) 45 | check_include_files(linux/random.h HAVE_LINUX_RANDOM_H) 46 | 47 | check_include_files(sys/capability.h HAVE_SYS_CAPABILITY_H) 48 | check_include_files(sys/capsicum.h HAVE_SYS_CAPSICUM_H) 49 | check_include_files(dlfcn.h HAVE_DLFCN_H) 50 | 51 | check_function_exists(explicit_bzero HAVE_EXPLICIT_BZERO) 52 | check_function_exists(memset_s HAVE_MEMSET_S) 53 | check_function_exists(valloc HAVE_VALLOC) 54 | check_function_exists(posix_memalign HAVE_POSIX_MEMALIGN) 55 | check_function_exists(aligned_alloc HAVE_ALIGNED_ALLOC) 56 | check_function_exists(arc4random_buf HAVE_ARC4RANDOM_BUF) 57 | check_function_exists(bcopy HAVE_BCOPY) 58 | check_function_exists(memmove HAVE_MEMMOVE) 59 | check_function_exists(getrandom HAVE_GETRANDOM) 60 | 61 | check_symbol_exists(O_NOFOLLOW "sys/types.h;sys/fcntl.h" HAVE_O_NOFOLLOW) 62 | 63 | check_library_exists(bsd readpassphrase "" HAVE_LIBBSD) 64 | 65 | if(HAVE_SYS_CAPSICUM_H) 66 | set(HAVE_CAPSICUM 1) 67 | endif() 68 | 69 | add_definitions(-DHAVE_CONFIG_H) 70 | 71 | add_library(libasignify 72 | include/asignify.h 73 | libasignify/asignify_internal.h 74 | libasignify/b64_pton.c 75 | libasignify/blake2-impl.h 76 | libasignify/blake2.h 77 | libasignify/blake2b-ref.c 78 | libasignify/chacha.c 79 | libasignify/chacha.h 80 | libasignify/databuf.c 81 | libasignify/encrypt.c 82 | libasignify/generate.c 83 | libasignify/khash.h 84 | libasignify/kvec.h 85 | libasignify/pbkdf2.c 86 | libasignify/pubkey.c 87 | libasignify/sha2.c 88 | libasignify/sha2.h 89 | libasignify/sign.c 90 | libasignify/signature.c 91 | libasignify/tweetnacl.c 92 | libasignify/tweetnacl.h 93 | libasignify/util.c 94 | libasignify/verify.c) 95 | set_target_properties(libasignify 96 | PROPERTIES PREFIX "") 97 | target_link_options(libasignify 98 | PUBLIC "LINKER:--version-script=${CMAKE_SOURCE_DIR}/libasignify/libasignify.ver") 99 | 100 | add_executable(asignify 101 | src/asignify.c 102 | src/cli.h 103 | src/encrypt.c 104 | src/generate.c 105 | src/readpassphrase_compat.h 106 | src/sign.c 107 | src/verify.c) 108 | target_link_libraries(asignify libasignify) 109 | 110 | if (HAVE_LIBBSD) 111 | target_link_libraries(libasignify bsd) 112 | endif() 113 | 114 | if(ENABLE_OPENSSL MATCHES "ON") 115 | find_package(OpenSSL REQUIRED) 116 | 117 | if(OpenSSL_FOUND) 118 | include_directories(${OPENSSL_INCLUDE_DIR}) 119 | link_directories(${OPENSSL_LIBRARIES}) 120 | message(STATUS "Using OpenSSL ${OPENSSL_VERSION}") 121 | set(HAVE_OPENSSL 1) 122 | else() 123 | message(FATAL_ERROR "Cannot find openssl") 124 | endif() 125 | 126 | target_link_libraries(libasignify ${OPENSSL_CRYPTO_LIBRARIES}) 127 | endif() 128 | configure_file("${CMAKE_SOURCE_DIR}/cmake/config.h.in" "${CMAKE_BINARY_DIR}/config.h") 129 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015, Vsevolod Stakhov 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | ACLOCAL_AMFLAGS = -I m4 2 | 3 | SUBDIRS=libasignify include src docs 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # asignify 2 | 3 | Yet another signify tool 4 | 5 | ## Introduction 6 | 7 | Asignify is heavily inspired by [signify](https://cvsweb.openbsd.org/cgi-bin/cvsweb/src/usr.bin/signify), used in OpenBSD. 8 | However, the main goal of this project is to define a high-level API for signing files, 9 | validating signatures and encrypting files using public key cryptography. 10 | Asignify is designed to be portable and self-contained with zero external dependencies. It uses [blake2b](https://blake2.net/) as the hash function and the ed25519 implementation from [tweetnacl](http://tweetnacl.cr.yp.to/). 11 | 12 | Asignify can verify OpenBSD signatures (but it cannot sign messages in OpenBSD format yet). 13 | 14 | ## Key Features 15 | 16 | - Zero dependencies (libc and C compiler are likely required though), so it could be easily used in embedded systems 17 | - Modern cryptography primitives (ed25519, blake2 and sha512 namely) 18 | - The ability to encrypt files with the same keys using curve25519-based [cryptobox](http://nacl.cr.yp.to/box.html). 19 | - Protecting secret keys with passwords using PBKDF2-BLAKE2 routines 20 | - `asignify` can convert ssh ed25519 private keys to the native format and verify signatures using just ssh ed25519 public keys (without intermediate conversions) 21 | - `asignify` is designed to be fast and portable. It is faster than many state-of-art tools, for example, gpg: 22 | 23 | ``` 24 | Intel(R) Core(TM)2 Quad CPU Q6600 @ 2.40GHz (-O3) 25 | 26 | asignify encrypt sec1 sec2.pub test 7,66s user 2,48s system 88% cpu 11,482 total 27 | gpg --encrypt --sign -r bob@example.com test 20,61s user 0,51s system 99% cpu 21,197 total 28 | ``` 29 | 30 | - `asignify` provides a high-level API to application developers for signing, verifying, encrypting and 31 | key generation 32 | - All keys, signatures and encrypted files contain version information, allowing changes to 33 | cryptographical primitives in the future without loss of backward compatibility. 34 | 35 | ## Usage Examples 36 | 37 | Here are some (descriptive) usage examples of the `asignify` utility: 38 | 39 | - Get help for a the tool: 40 | 41 | ``` 42 | $ asignify help 43 | $ asignify help 44 | ``` 45 | 46 | - Generate a Keypair: 47 | 48 | ``` 49 | $ asignify generate privkey 50 | $ asignify generate --no-password privkey pubkey 51 | ``` 52 | 53 | - Convert an SSH Key: 54 | 55 | ``` 56 | $ asignify generate -s sshkey privkey 57 | $ asignify generate --no-password -s sshkey privkey 58 | ``` 59 | 60 | - Sign Files 61 | 62 | ``` 63 | $ asignify sign secretkey digests.sig file1 file2 ... 64 | ``` 65 | 66 | - Verify a Digest File's Signature 67 | 68 | ``` 69 | $ asignify verify publickey digests.sig 70 | ``` 71 | 72 | - Check the Integrity of Files Correspoinding to the Digests 73 | 74 | ``` 75 | $ asignify check publickey digests.sig file1 file2 ... 76 | ``` 77 | 78 | - Check Integrity Using an SSH Key 79 | 80 | ``` 81 | $ asignify check sshpubkey digests.sig file1 file2 ... 82 | ``` 83 | 84 | - Encrypt a File Using Your Private Key and a Peer's Public Key: 85 | 86 | ``` 87 | $ asignify encrypt ownprivkey peerpubkey in out 88 | ``` 89 | 90 | - Decrypt a File Using a Peer's Private Key and Your Public Key: 91 | 92 | ``` 93 | $ asignify decrypt peerprivkey ownpubkey in out 94 | $ asignify encrypt -d peerprivkey ownpubkey in out 95 | ``` 96 | 97 | ## Cryptographic Basis 98 | 99 | Asignify relies on the same primitives as the `signify` utility. However, for the native format, 100 | asignify uses the `blake2` cryptographic hash function instead of `sha512`. I decided this 101 | mainly because of the performance of `blake2`. Since this function was in the final of 102 | SHA3 competition (along with the current `keccak` winner), I believe that it is secure 103 | enough for use as a collision-resistant hash function. Moreover, unlike sha2, blake2 is 104 | not vulnerable to extension attacks. 105 | 106 | For digital signatures, `asignify` uses the `ed25519` algorithm which is blazingly fast and 107 | proven to be secure even without a random oracle (based on Schnorr scheme). The `tweetnacl` library 108 | is very small and precisely analyzed. 109 | 110 | To sign a file, `asignify` does the following steps: 111 | 112 | 1. Calculates the digest of a file (e.g. `blake2` or `sha512`) 113 | 2. Opens the secret key file (decrypting it if needed) 114 | 3. Write all the digests to the output buffer in format: 115 | 116 | ``` 117 | SHA256 (filename) = deadbeef.... 118 | SIZE (filename) = 666 119 | ... 120 | ``` 121 | 122 | 4. Calculates the ed25519 signature over using secret key and the following fields: 123 | - version 124 | - data 125 | 5. Afterwards, a signature is packed into the `asignify` signature line and prepended 126 | to the digests content 127 | 128 | To verify a signature, `asignify` loads the public key, verifies the signature in the same 129 | way, loads the files' digest(s) and verifies the corresponding files agains those digests. 130 | 131 | Hence, `asignify` only signs digests of files and not files themselves, and a signature 132 | contains both digests and its ed25519 signature. 133 | 134 | ## Key Storage 135 | 136 | The secret key for `asignify` can be encrypted using a password-based key derivation function, 137 | namely `pbkdf2-blake2`. This function can be tuned for the number of rounds to increase 138 | amount of work required for an adversary to brute force the encryption password into 139 | a valid encryption key. 140 | 141 | Currently, `asignify` uses the following fields for private keys: 142 | 143 | ~~~ 144 | asignify-private-key 145 | version: 1 146 | data: 147 | id: 148 | kdf: pbkdf2-blake2 149 | rounds: 42000 150 | salt: 151 | checksum: 152 | ~~~ 153 | 154 | The checksum is used to validate the password against the original encryption key. The current 155 | minimum rounds count is 10000. However, it can be changed in future. 156 | 157 | Public keys and signatures have nearly the same format: 158 | 159 | ~~~ 160 | magic:version:: 161 | asignify-pubkey:1:kEjp3MrX5fE=:Hut...bl/mQ= 162 | asignify-sig:1:kEjp3MrX5fE=:Sfg...A== 163 | ~~~ 164 | 165 | The key ID is used to match keypairs and the corresponding signatures. 166 | 167 | ## Libasignify API 168 | 169 | `libasignify` provides a high-level API for the most common signature operations. 170 | 171 | To verify a signature, you should do the following: 172 | 173 | ~~~C 174 | /* Init context */ 175 | vrf = asignify_verify_init(); 176 | 177 | /* Load pubkey */ 178 | if (!asignify_verify_load_pubkey(vrf, pubkeyfile)) { 179 | errx(1, "cannot load pubkey %s: %s", pubkeyfile, 180 | asignify_verify_get_error(vrf)); 181 | } 182 | 183 | /* Load and verify digests file */ 184 | if (!asignify_verify_load_signature(vrf, sigfile)) { 185 | errx(1, "cannot load signature %s: %s", sigfile, 186 | asignify_verify_get_error(vrf)); 187 | } 188 | 189 | /* Verify files with digests */ 190 | for (i = 0; i < argc; i ++) { 191 | if (!asignify_verify_file(vrf, argv[i])) { 192 | errx(1, "cannot verify file %s: %s", argv[i], 193 | asignify_verify_get_error(vrf)); 194 | } 195 | } 196 | 197 | /* Cleanup */ 198 | asignify_verify_free(vrf); 199 | ~~~ 200 | 201 | To sign files, you should provide a callback for the password prompt (e.g. by BSD function 202 | `readpassphrase`): 203 | 204 | ~~~C 205 | static int 206 | read_password(char *buf, size_t len, void *d) 207 | { 208 | char password[512]; 209 | int l; 210 | 211 | if (readpassphrase("Password:", password, sizeof(password), 0) != NULL) { 212 | l = strlen(password); 213 | memcpy(buf, password, l); 214 | 215 | /* Securely clean password data */ 216 | explicit_memzero(password, sizeof(password)); 217 | 218 | return (l); 219 | } 220 | 221 | return (-1); 222 | } 223 | ~~~ 224 | 225 | If you want to use unencrypted private keys, then just pass NULL as a password 226 | callback when trying to open a secret key file. 227 | 228 | Afterwards, signing is not so hard: 229 | 230 | ~~~C 231 | 232 | /* Init sign context */ 233 | sgn = asignify_sign_init(); 234 | 235 | /* Load encrypted private key with the provided password callback */ 236 | if (!asignify_sign_load_privkey(sgn, seckeyfile, read_password, NULL)) { 237 | errx(1, "cannot load private key %s: %s", seckeyfile, 238 | asignify_sign_get_error(sgn)); 239 | } 240 | 241 | /* Add files digests */ 242 | for (i = 0; i < argc; i ++) { 243 | if (!asignify_sign_add_file(sgn, argv[i], ASIGNIFY_DIGEST_BLAKE2)) { 244 | errx(1, "cannot sign file %s: %s", argv[i], 245 | asignify_sign_get_error(sgn)); 246 | } 247 | } 248 | 249 | /* Sign digests and write everything to a file */ 250 | if (!asignify_sign_write_signature(sgn, sigfile)) { 251 | errx(1, "cannot write sign file %s: %s", sigfile, 252 | asignify_sign_get_error(sgn)); 253 | } 254 | 255 | /* Cleanup */ 256 | asignify_sign_free(sgn); 257 | ~~~ 258 | 259 | Generation of keypairs is served by `libasignify` as well: 260 | 261 | ~~~C 262 | if (!asignify_generate(seckeyfile, pubkeyfile, 1, rounds, 263 | read_password_verify, NULL)) { 264 | errx(1, "Cannot generate keypair"); 265 | } 266 | ~~~ 267 | 268 | Specifying `NULL` as a password callback leads to unencrypted secret keys being produced. 269 | 270 | 271 | ## Supported Digest Formats 272 | 273 | - SHA256 274 | - SHA512 275 | - BLAKE2b 276 | 277 | For sha2, `libasignify` can use openssl if it is available in the system since it 278 | provides highly optimized versions of SHA that calculates checksums much quicker 279 | than the sha2 code embedded into `libasignify`. 280 | 281 | ## OpenBSD Signatures 282 | 283 | `libasignify` automatically recognises and parses OpenBSD signatures and public keys, allowing 284 | verification of signatures produced by the `signify` utility transparently. Secret keys and signing 285 | are currently unsupported, however support is planned in the future. 286 | 287 | ## Roadmap 288 | 289 | - Better OpenBSD compatibility 290 | - ~~Better CLI~~ 291 | - ~~Manpages and other docs~~ 292 | - Fuzz testing 293 | - ~~Encryption via ed25519 <-> curve25519 transform~~ 294 | 295 | ## License and Authors 296 | 297 | This code is licensed under simplified BSD license and includes portions of third 298 | party code designed and written by various authors: 299 | 300 | - blake2: 301 | + Jean-Philippe Aumasson 302 | + Christian Winnerlein 303 | + Samuel Neves 304 | + Zooko Wilcox-O'Hearn 305 | - chacha20 306 | + Daniel J. Bernstein 307 | - salsa20 308 | + Daniel J. Bernstein 309 | - curve25519 310 | + Daniel J. Bernstein 311 | - curve25519xsalsa20poly1305 312 | + Daniel J. Bernstein 313 | - ed25519 314 | + Daniel J. Bernstein 315 | + Bo-Yin Yang 316 | + Niels Duif 317 | + Peter Schwabe 318 | + Tanja Lange 319 | - chacha20 implementation 320 | + Andrew "floodyberry" Moon. 321 | -------------------------------------------------------------------------------- /autogen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | autoreconf -i 3 | -------------------------------------------------------------------------------- /cmake/config.h.in: -------------------------------------------------------------------------------- 1 | #ifndef LIBASIGNIFY_CONFIG_H 2 | #define LIBASIGNIFY_CONFIG_H 3 | 4 | #define VERSION "${LIBASIGNIFY_VERSION}" 5 | 6 | /* Define to 1 if you have the `aligned_alloc' function. */ 7 | #cmakedefine HAVE_ALIGNED_ALLOC 1 8 | 9 | /* Define to 1 if you have the `arc4random_buf' function. */ 10 | #cmakedefine HAVE_ARC4RANDOM_BUF 1 11 | 12 | /* Define to 1 if you have the `bcopy' function. */ 13 | #cmakedefine HAVE_BCOPY 1 14 | 15 | /* Define to 1 if you have the header file. */ 16 | #cmakedefine HAVE_BSD_ERR_H 1 17 | 18 | /* Define to 1 if you have the header file. */ 19 | #cmakedefine HAVE_BSD_LIBUTIL_H 1 20 | 21 | /* Define to 1 if you have the header file. */ 22 | #cmakedefine HAVE_BSD_READPASSPHRASE_H 1 23 | 24 | /* Define tot 1 if you have the header file. */ 25 | #cmakedefine HAVE_LINUX_RANDOM_H 1 26 | 27 | /* Define 1 if you have 'capsicum'. */ 28 | #cmakedefine HAVE_CAPSICUM 1 29 | 30 | /* Define to 1 if you have the header file. */ 31 | #cmakedefine HAVE_DLFCN_H 1 32 | 33 | /* Define to 1 if you have the `explicit_bzero' function. */ 34 | #cmakedefine HAVE_EXPLICIT_BZERO 1 35 | 36 | /* Use getrandom for entropy gain */ 37 | #cmakedefine HAVE_GETRANDOM 1 38 | 39 | /* Define to 1 if you have the header file. */ 40 | #cmakedefine HAVE_INTTYPES_H 1 41 | 42 | /* Define to 1 if you have the `bsd' library (-lbsd). */ 43 | #cmakedefine HAVE_LIBBSD 1 44 | 45 | /* Define to 1 if you have the `memmove' function. */ 46 | #cmakedefine HAVE_MEMMOVE 1 47 | 48 | /* Define to 1 if you have the header file. */ 49 | #cmakedefine HAVE_MEMORY_H 1 50 | 51 | /* Define to 1 if you have the `memset_s' function. */ 52 | #cmakedefine HAVE_MEMSET_S 1 53 | 54 | /* Define 1 if you have openssl. */ 55 | #cmakedefine HAVE_OPENSSL 1 56 | 57 | /* Use O_NOFOLLOW for open */ 58 | #cmakedefine HAVE_O_NOFOLLOW 1 59 | 60 | /* Define to 1 if you have the `posix_memalign' function. */ 61 | #cmakedefine HAVE_POSIX_MEMALIGN 1 62 | 63 | /* Define to 1 if you have the header file. */ 64 | #cmakedefine HAVE_READPASSPHRASE_H 1 65 | 66 | /* Define to 1 if you have the header file. */ 67 | #cmakedefine HAVE_STDARG_H 1 68 | 69 | /* Define to 1 if you have the header file. */ 70 | #cmakedefine HAVE_STDBOOL_H 1 71 | 72 | /* Define to 1 if you have the header file. */ 73 | #cmakedefine HAVE_STDDEF_H 1 74 | 75 | /* Define to 1 if you have the header file. */ 76 | #cmakedefine HAVE_STDINT_H 1 77 | 78 | /* Define to 1 if you have the header file. */ 79 | #cmakedefine HAVE_STDLIB_H 1 80 | 81 | /* Define to 1 if you have the header file. */ 82 | #cmakedefine HAVE_STRINGS_H 1 83 | 84 | /* Define to 1 if you have the header file. */ 85 | #cmakedefine HAVE_STRING_H 1 86 | 87 | /* Define to 1 if you have the header file. */ 88 | #cmakedefine HAVE_SYS_CAPABILITY_H 1 89 | 90 | /* Define to 1 if you have the header file. */ 91 | #cmakedefine HAVE_SYS_CAPSICUM_H 1 92 | 93 | /* Define to 1 if you have the header file. */ 94 | #cmakedefine HAVE_SYS_STAT_H 1 95 | 96 | /* Define to 1 if you have the header file. */ 97 | #cmakedefine HAVE_SYS_RANDOM_H 1 98 | 99 | /* Define to 1 if you have the header file. */ 100 | #cmakedefine HAVE_SYS_TYPES_H 1 101 | 102 | /* Define to 1 if you have the header file. */ 103 | #cmakedefine HAVE_UNISTD_H 1 104 | 105 | /* Define to 1 if you have the `valloc' function. */ 106 | #cmakedefine HAVE_VALLOC 1 107 | 108 | /* weak symbols are supported */ 109 | #cmakedefine HAVE_WEAK_SYMBOLS 1 110 | 111 | #endif 112 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | AC_PREREQ(2.59) 2 | AC_INIT(asignify, 1.1) 3 | 4 | AC_CANONICAL_SYSTEM 5 | 6 | ASIGNIFY_LIBRARY_VERSION=2:0:0 7 | # | | | 8 | # +------+ | +---+ 9 | # | | | 10 | # current:revision:age 11 | # | | | 12 | # | | +- increment if interfaces have been added 13 | # | | set to zero if interfaces have been removed 14 | # | | or changed 15 | # | +- increment if source code has changed 16 | # | set to zero if current is incremented 17 | # +- increment if interfaces have been added, removed or changed 18 | AC_SUBST(ASIGNIFY_LIBRARY_VERSION) 19 | 20 | AM_INIT_AUTOMAKE([1.11 foreign -Wno-portability no-dist-gzip dist-xz]) 21 | AM_SILENT_RULES([yes]) 22 | 23 | dnl Initialize Libtool 24 | LT_INIT 25 | AC_CONFIG_MACRO_DIR([m4]) 26 | AC_PROG_CC_C99 27 | AX_CFLAGS_WARN_ALL 28 | AM_PROG_CC_C_O 29 | AC_C_CONST 30 | AC_TYPE_SIZE_T 31 | AC_CHECK_FUNCS(memmove bcopy) 32 | AC_PROG_LN_S 33 | 34 | AC_CHECK_HEADERS_ONCE([stdlib.h]) 35 | AC_CHECK_HEADERS_ONCE([stddef.h]) 36 | AC_CHECK_HEADERS_ONCE([stdarg.h]) 37 | AC_CHECK_HEADERS_ONCE([stdbool.h]) 38 | AC_CHECK_HEADERS_ONCE([stdint.h]) 39 | AC_CHECK_HEADERS_ONCE([string.h]) 40 | AC_CHECK_HEADERS_ONCE([unistd.h]) 41 | AC_CHECK_HEADERS_ONCE([readpassphrase.h]) 42 | 43 | dnl BSD library on Linux systems 44 | AC_CANONICAL_HOST 45 | case $host_os in 46 | linux*) 47 | OS_CFLAGS="-D_XOPEN_SOURCE -D_GNU_SOURCE" 48 | OS_LDFLAGS="" 49 | OS_LIBS="-ldl -lrt" 50 | ;; 51 | *) 52 | OS_CFLAGS="-D_BSD_SOURCE -D_WITH_GETLINE" 53 | OS_LDFLAGS= 54 | OS_LIBS= 55 | ;; 56 | esac 57 | AC_SUBST(OS_CFLAGS) 58 | AC_SUBST(OS_LDFLAGS) 59 | AC_SUBST(OS_LIBS) 60 | AC_CHECK_HEADERS_ONCE([bsd/stdlib.h]) 61 | AC_CHECK_HEADERS_ONCE([bsd/string.h]) 62 | AC_CHECK_HEADERS_ONCE([bsd/stdio.h]) 63 | AC_CHECK_HEADERS_ONCE([bsd/readpassphrase.h]) 64 | AC_CHECK_HEADERS_ONCE([bsd/libutil.h]) 65 | AC_CHECK_HEADERS_ONCE([bsd/err.h]) 66 | 67 | AC_CHECK_DECL([O_NOFOLLOW], 68 | AC_DEFINE([HAVE_O_NOFOLLOW], [1], [Use O_NOFOLLOW for open]), 69 | [], 70 | [#include 71 | #include 72 | #include 73 | ] 74 | ) 75 | 76 | AC_CHECK_DECL([getrandom], 77 | AC_DEFINE([HAVE_GETRANDOM], [1], [Use getrandom for entropy gain]), 78 | [], 79 | [#include 80 | #include 81 | #include 82 | #include 83 | ] 84 | ) 85 | 86 | AC_CHECK_FUNCS([explicit_bzero memset_s]) 87 | AC_MSG_CHECKING(if weak symbols are supported) 88 | AC_LINK_IFELSE([AC_LANG_PROGRAM([[ 89 | __attribute__((weak)) void __dummy(void *x) { } 90 | void f(void *x) { __dummy(x); } 91 | ]], [[ ]] 92 | )], 93 | [AC_MSG_RESULT(yes) 94 | AC_DEFINE([HAVE_WEAK_SYMBOLS], [1], [weak symbols are supported])], 95 | [AC_MSG_RESULT(no)]) 96 | 97 | AC_CHECK_FUNCS([posix_memalign aligned_alloc valloc]) 98 | 99 | dnl Capsicum support 100 | AC_CHECK_HEADERS_ONCE([sys/capability.h]) 101 | AC_CHECK_HEADERS_ONCE([sys/capsicum.h]) 102 | AC_CHECK_LIB(c, cap_sandboxed, [ 103 | AC_DEFINE(HAVE_CAPSICUM, 1, [Define 1 if you have 'capsicum'.]) 104 | ]) 105 | 106 | AC_CHECK_LIB([bsd], [readpassphrase]) 107 | AC_SEARCH_LIBS([arc4random_buf], [bsd]) 108 | AC_CHECK_FUNCS([arc4random_buf]) 109 | 110 | AC_ARG_ENABLE([openssl], 111 | AS_HELP_STRING([--enable-openssl], [Use openssl for faster hashes computation])) 112 | AS_IF([test "x$enable_openssl" = "xyes"], 113 | [AX_CHECK_OPENSSL([ 114 | AC_DEFINE(HAVE_OPENSSL, 1, [Define 1 if you have openssl.])], [] 115 | )] 116 | ) 117 | 118 | dnl Check if Libtool is present 119 | dnl Libtool is used for building share libraries 120 | AC_PROG_LIBTOOL 121 | 122 | AC_CONFIG_FILES(Makefile 123 | src/Makefile 124 | libasignify/Makefile 125 | include/Makefile 126 | docs/Makefile) 127 | AC_CONFIG_HEADERS(config.h) 128 | AC_OUTPUT 129 | 130 | -------------------------------------------------------------------------------- /docs/Makefile.am: -------------------------------------------------------------------------------- 1 | dist_man_MANS= asignify.1 2 | EXTRA_DIST= asignify.pod 3 | 4 | mandir= $(prefix)/man -------------------------------------------------------------------------------- /docs/asignify.1: -------------------------------------------------------------------------------- 1 | .\" Automatically generated by Pod::Man 4.14 (Pod::Simple 3.40) 2 | .\" 3 | .\" Standard preamble: 4 | .\" ======================================================================== 5 | .de Sp \" Vertical space (when we can't use .PP) 6 | .if t .sp .5v 7 | .if n .sp 8 | .. 9 | .de Vb \" Begin verbatim text 10 | .ft CW 11 | .nf 12 | .ne \\$1 13 | .. 14 | .de Ve \" End verbatim text 15 | .ft R 16 | .fi 17 | .. 18 | .\" Set up some character translations and predefined strings. \*(-- will 19 | .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left 20 | .\" double quote, and \*(R" will give a right double quote. \*(C+ will 21 | .\" give a nicer C++. Capital omega is used to do unbreakable dashes and 22 | .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, 23 | .\" nothing in troff, for use with C<>. 24 | .tr \(*W- 25 | .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' 26 | .ie n \{\ 27 | . ds -- \(*W- 28 | . ds PI pi 29 | . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch 30 | . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch 31 | . ds L" "" 32 | . ds R" "" 33 | . ds C` "" 34 | . ds C' "" 35 | 'br\} 36 | .el\{\ 37 | . ds -- \|\(em\| 38 | . ds PI \(*p 39 | . ds L" `` 40 | . ds R" '' 41 | . ds C` 42 | . ds C' 43 | 'br\} 44 | .\" 45 | .\" Escape single quotes in literal strings from groff's Unicode transform. 46 | .ie \n(.g .ds Aq \(aq 47 | .el .ds Aq ' 48 | .\" 49 | .\" If the F register is >0, we'll generate index entries on stderr for 50 | .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index 51 | .\" entries marked with X<> in POD. Of course, you'll have to process the 52 | .\" output yourself in some meaningful fashion. 53 | .\" 54 | .\" Avoid warning from groff about undefined register 'F'. 55 | .de IX 56 | .. 57 | .nr rF 0 58 | .if \n(.g .if rF .nr rF 1 59 | .if (\n(rF:(\n(.g==0)) \{\ 60 | . if \nF \{\ 61 | . de IX 62 | . tm Index:\\$1\t\\n%\t"\\$2" 63 | .. 64 | . if !\nF==2 \{\ 65 | . nr % 0 66 | . nr F 2 67 | . \} 68 | . \} 69 | .\} 70 | .rr rF 71 | .\" 72 | .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). 73 | .\" Fear. Run. Save yourself. No user-serviceable parts. 74 | . \" fudge factors for nroff and troff 75 | .if n \{\ 76 | . ds #H 0 77 | . ds #V .8m 78 | . ds #F .3m 79 | . ds #[ \f1 80 | . ds #] \fP 81 | .\} 82 | .if t \{\ 83 | . ds #H ((1u-(\\\\n(.fu%2u))*.13m) 84 | . ds #V .6m 85 | . ds #F 0 86 | . ds #[ \& 87 | . ds #] \& 88 | .\} 89 | . \" simple accents for nroff and troff 90 | .if n \{\ 91 | . ds ' \& 92 | . ds ` \& 93 | . ds ^ \& 94 | . ds , \& 95 | . ds ~ ~ 96 | . ds / 97 | .\} 98 | .if t \{\ 99 | . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" 100 | . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' 101 | . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' 102 | . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' 103 | . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' 104 | . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' 105 | .\} 106 | . \" troff and (daisy-wheel) nroff accents 107 | .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' 108 | .ds 8 \h'\*(#H'\(*b\h'-\*(#H' 109 | .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] 110 | .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' 111 | .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' 112 | .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] 113 | .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] 114 | .ds ae a\h'-(\w'a'u*4/10)'e 115 | .ds Ae A\h'-(\w'A'u*4/10)'E 116 | . \" corrections for vroff 117 | .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' 118 | .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' 119 | . \" for low resolution devices (crt and lpr) 120 | .if \n(.H>23 .if \n(.V>19 \ 121 | \{\ 122 | . ds : e 123 | . ds 8 ss 124 | . ds o a 125 | . ds d- d\h'-1'\(ga 126 | . ds D- D\h'-1'\(hy 127 | . ds th \o'bp' 128 | . ds Th \o'LP' 129 | . ds ae ae 130 | . ds Ae AE 131 | .\} 132 | .rm #[ #] #H #V #F C 133 | .\" ======================================================================== 134 | .\" 135 | .IX Title "ASIGNIFY 1" 136 | .TH ASIGNIFY 1 "2021-02-13" "perl v5.32.0" "User Contributed Perl Documentation" 137 | .\" For nroff, turn off justification. Always turn off hyphenation; it makes 138 | .\" way too many mistakes in technical documents. 139 | .if n .ad l 140 | .nh 141 | .SH "NAME" 142 | asignify \- cryptographically sign, verify, encrypt or decrypt files. 143 | .SH "SYNOPSIS" 144 | .IX Header "SYNOPSIS" 145 | \&\fBasignify\fR [\fB\-q\fR] verify pubkey signature 146 | .PP 147 | \&\fBasignify\fR [\fB\-q\fR] check pubkey signature file [file...] 148 | .PP 149 | \&\fBasignify\fR [\fB\-q\fR] sign [\fB\-n\fR] [\fB\-d\fR\ \fIdigest\fR] [\fB\-s\fR\ \fIsshkey\fR] secretkey signature [file1\ [file2...]] 150 | .PP 151 | \&\fBasignify\fR [\fB\-q\fR] generate [\fB\-n\fR] [\fB\-p\fR] [\fB\-r\fR\ \fIrounds\fR] secretkey [publickey] 152 | .PP 153 | \&\fBasignify\fR [\fB\-q\fR] encrypt [\fB\-d\fR] secretkey publickey infile outfile 154 | .PP 155 | \&\fBasignify\fR [\fB\-q\fR] decrypt secretkey publickey infile outfile 156 | .SH "DESCRIPTION" 157 | .IX Header "DESCRIPTION" 158 | The asignify utility creates and verifies cryptographic signatures. A signature is stamped on a digests file 159 | that contains hash digests of files using various hash functions (namely, sha256, sha512 and blake2b). 160 | .PP 161 | The mode of operation is selected with the following options: 162 | .IP "\fB\-q\fR" 8 163 | .IX Item "-q" 164 | Quiet mode. Suppress informational output. 165 | .IP "\fBverify\fR" 8 166 | .IX Item "verify" 167 | Verify signarure for a digests file (but do not verify digests themselves): 168 | .RS 8 169 | .IP "\fBpubkey\fR" 12 170 | .IX Item "pubkey" 171 | Name of the file with a public key. 172 | .IP "\fBsignature\fR" 12 173 | .IX Item "signature" 174 | Name of signature file. 175 | .RE 176 | .RS 8 177 | .RE 178 | .IP "\fBcheck\fR" 8 179 | .IX Item "check" 180 | Verify a signed digests list, and then verify the checksum for each file listed in the arguments and specified in the digests list: 181 | .RS 8 182 | .IP "\fBpubkey\fR" 12 183 | .IX Item "pubkey" 184 | Name of the file with a public key. 185 | .IP "\fBsignature\fR" 12 186 | .IX Item "signature" 187 | Name of a signature file. 188 | .IP "\fBfile\fR" 12 189 | .IX Item "file" 190 | List of files whose digests need to be verified. 191 | .RE 192 | .RS 8 193 | .RE 194 | .IP "\fBgenerate\fR" 8 195 | .IX Item "generate" 196 | Generate a new key pair of secret and public keys: 197 | .RS 8 198 | .IP "\fB\-n, \-\-no\-password\fR" 12 199 | .IX Item "-n, --no-password" 200 | Do not ask for a passphrase during key generation. Otherwise, \fBasignify\fR will prompt the user for a passphrase to encrypt the secret key with. 201 | .IP "\fB\-p, \-\-pubkey\-only\fR" 12 202 | .IX Item "-p, --pubkey-only" 203 | Generate just the public key component for the given secret key. This argument is not compatible with \fB\-s\fR. The \fB\-n\fR and \fB\-r\fR arguments are ignored. 204 | .IP "\fB\-r, \-\-rounds\fR" 12 205 | .IX Item "-r, --rounds" 206 | Indicate a number of iterations (rounds) used by \s-1PBKDF\s0 algorithm (default number of rounds: 10000). 207 | .IP "\fB\-s, \-\-ssh\fR" 12 208 | .IX Item "-s, --ssh" 209 | Convert \fBunencrypted\fR ed25519 private key generated by openssh to the native asignify format. The target key could be encrypted as usually. 210 | .IP "\fBsecretkey\fR" 12 211 | .IX Item "secretkey" 212 | Mandatory path to file where secret key will be written, or read in the case of \fB\-p\fR. 213 | .IP "\fBpubkey\fR" 12 214 | .IX Item "pubkey" 215 | Optional path to file where public key will be writed and by default will be generated from as \fI[secretkey]\fR.pub. This option is not used with ssh keys. 216 | .RE 217 | .RS 8 218 | .RE 219 | .IP "\fBsign\fR" 8 220 | .IX Item "sign" 221 | Calculate digests for the files specified and create a signed digests file: 222 | .RS 8 223 | .IP "\fB\-n, \-\-no\-size\fR" 12 224 | .IX Item "-n, --no-size" 225 | Do not record files sizes in signature file. 226 | .IP "\fB\-d, \-\-digest\fR" 12 227 | .IX Item "-d, --digest" 228 | Indicate a hash function which will be used for singing. Currently the asignify has support of following hashes: 229 | \&\fBsha256\fR\|(1), \fBsha512\fR\|(1), blake2 (default if none is defined). It is possible to specify multiple \fB\-d\fR options to calculate multiple 230 | checksums for each file. 231 | .IP "\fBsecretkey\fR" 12 232 | .IX Item "secretkey" 233 | Name of the file with a secret key. 234 | .IP "\fBsignature\fR" 12 235 | .IX Item "signature" 236 | Name of file where signed digests will be stored. 237 | .IP "\fBfile\fR" 12 238 | .IX Item "file" 239 | List of file(s) to calculate digests for. 240 | .RE 241 | .RS 8 242 | .RE 243 | .IP "\fBencrypt\fR" 8 244 | .IX Item "encrypt" 245 | Encrypt a file using local private key and remote public key (and vice-versa for decryption): 246 | .RS 8 247 | .IP "\fB\-d, \-\-decrypt\fR" 12 248 | .IX Item "-d, --decrypt" 249 | Decrypt using remote privkey and local pubkey (that is same as invoking this command as \fBdecrypt\fR) 250 | .IP "\fB\-f, \-\-fast\fR" 12 251 | .IX Item "-f, --fast" 252 | Use faster encryption algorithm (namely chacha8 instead of chacha20). It might be useful for embedded plaforms still providing reasonable level of security. 253 | .IP "\fBsecretkey\fR" 12 254 | .IX Item "secretkey" 255 | Name of the file with a secret key: local for encryption and remote for decryption. 256 | .IP "\fBpublickey\fR" 12 257 | .IX Item "publickey" 258 | Name of the file with a public key: remote for encryption and local for decryption. 259 | .IP "\fBin\fR" 12 260 | .IX Item "in" 261 | The name of input file. 262 | .IP "\fBout\fR" 12 263 | .IX Item "out" 264 | The name of output file. 265 | .RE 266 | .RS 8 267 | .RE 268 | .SH "EXIT STATUS" 269 | .IX Header "EXIT STATUS" 270 | The asignify return zero exit code on success, and non-zero if an error occurs. 271 | It may fail because of one of the following reasons: 272 | .IP "\- Some files requested are absent." 4 273 | .IX Item "- Some files requested are absent." 274 | .PD 0 275 | .IP "\- Passphrase is incorrect (or passphrase and verification are not equal)." 4 276 | .IX Item "- Passphrase is incorrect (or passphrase and verification are not equal)." 277 | .IP "\- The message file has been corrupted and its signature is no longer valid." 4 278 | .IX Item "- The message file has been corrupted and its signature is no longer valid." 279 | .PD 280 | .SH "EXAMPLES" 281 | .IX Header "EXAMPLES" 282 | \&\fICreate a new key pair:\fR 283 | .PP 284 | .Vb 1 285 | \& $ asignify generate keys/key.secret keys/key.public 286 | .Ve 287 | .PP 288 | \&\fISign a file, specifying a signature name:\fR 289 | .PP 290 | .Vb 1 291 | \& $ asignify sign \-d blake2 keys/key.secret motd.sig /etc/motd 292 | .Ve 293 | .PP 294 | \&\fIVerify a signature:\fR 295 | .PP 296 | .Vb 1 297 | \& $ asignify verify keys/key.public motd.sig 298 | .Ve 299 | .PP 300 | \&\fIVerify a signed digest list:\fR 301 | .PP 302 | .Vb 1 303 | \& $ asignify check keys/key.public motd.sig /etc/motd 304 | .Ve 305 | -------------------------------------------------------------------------------- /docs/asignify.pod: -------------------------------------------------------------------------------- 1 | =head1 NAME 2 | 3 | asignify - cryptographically sign, verify, encrypt or decrypt files. 4 | 5 | =head1 SYNOPSIS 6 | 7 | B S<[B<-q>]> verify pubkey signature 8 | 9 | B S<[B<-q>]> check pubkey signature file S<[file...]> 10 | 11 | B S<[B<-q>]> sign S<[B<-n>]> S<[B<-d>S< I>]> S<[B<-s>S< I>]> secretkey signature S<[file1 S<[file2...]>]> 12 | 13 | B S<[B<-q>]> generate S<[B<-n>]> S<[B<-p>]> S<[B<-r>S< I>]> secretkey S<[publickey]> 14 | 15 | B S<[B<-q>]> encrypt S<[B<-d>]> secretkey publickey infile outfile 16 | 17 | B S<[B<-q>]> decrypt secretkey publickey infile outfile 18 | 19 | =head1 DESCRIPTION 20 | 21 | The asignify utility creates and verifies cryptographic signatures. A signature is stamped on a digests file 22 | that contains hash digests of files using various hash functions (namely, sha256, sha512 and blake2b). 23 | 24 | The mode of operation is selected with the following options: 25 | 26 | =over 8 27 | 28 | =item B<-q> 29 | 30 | Quiet mode. Suppress informational output. 31 | 32 | =item B 33 | 34 | Verify signarure for a digests file (but do not verify digests themselves): 35 | 36 | =over 12 37 | 38 | =item B 39 | 40 | Name of the file with a public key. 41 | 42 | =item B 43 | 44 | Name of signature file. 45 | 46 | =back 47 | 48 | =item B 49 | 50 | Verify a signed digests list, and then verify the checksum for each file listed in the arguments and specified in the digests list: 51 | 52 | =over 12 53 | 54 | =item B 55 | 56 | Name of the file with a public key. 57 | 58 | =item B 59 | 60 | Name of a signature file. 61 | 62 | =item B 63 | 64 | List of files whose digests need to be verified. 65 | 66 | =back 67 | 68 | =item B 69 | 70 | Generate a new key pair of secret and public keys: 71 | 72 | =over 12 73 | 74 | =item B<-n, --no-password> 75 | 76 | Do not ask for a passphrase during key generation. Otherwise, B will prompt the user for a passphrase to encrypt the secret key with. 77 | 78 | =item B<-p, --pubkey-only> 79 | 80 | Generate just the public key component for the given secret key. This argument is not compatible with B<-s>. The B<-n> and B<-r> arguments are ignored. 81 | 82 | =item B<-r, --rounds> 83 | 84 | Indicate a number of iterations (rounds) used by PBKDF algorithm (default number of rounds: 10000). 85 | 86 | =item B<-s, --ssh> 87 | 88 | Convert B ed25519 private key generated by openssh to the native asignify format. The target key could be encrypted as usually. 89 | 90 | =item B 91 | 92 | Mandatory path to file where secret key will be written, or read in the case of B<-p>. 93 | 94 | =item B 95 | 96 | Optional path to file where public key will be writed and by default will be generated from as I<[secretkey]>.pub. This option is not used with ssh keys. 97 | 98 | =back 99 | 100 | =item B 101 | 102 | Calculate digests for the files specified and create a signed digests file: 103 | 104 | =over 12 105 | 106 | =item B<-n, --no-size> 107 | 108 | Do not record files sizes in signature file. 109 | 110 | =item B<-d, --digest> 111 | 112 | Indicate a hash function which will be used for singing. Currently the asignify has support of following hashes: 113 | sha256(1), sha512(1), blake2 (default if none is defined). It is possible to specify multiple B<-d> options to calculate multiple 114 | checksums for each file. 115 | 116 | =item B 117 | 118 | Name of the file with a secret key. 119 | 120 | =item B 121 | 122 | Name of file where signed digests will be stored. 123 | 124 | =item B 125 | 126 | List of file(s) to calculate digests for. 127 | 128 | =back 129 | 130 | =item B 131 | 132 | Encrypt a file using local private key and remote public key (and vice-versa for decryption): 133 | 134 | =over 12 135 | 136 | =item B<-d, --decrypt> 137 | 138 | Decrypt using remote privkey and local pubkey (that is same as invoking this command as B) 139 | 140 | =item B<-f, --fast> 141 | 142 | Use faster encryption algorithm (namely chacha8 instead of chacha20). It might be useful for embedded plaforms still providing reasonable level of security. 143 | 144 | =item B 145 | 146 | Name of the file with a secret key: local for encryption and remote for decryption. 147 | 148 | =item B 149 | 150 | Name of the file with a public key: remote for encryption and local for decryption. 151 | 152 | =item B 153 | 154 | The name of input file. 155 | 156 | =item B 157 | 158 | The name of output file. 159 | 160 | =back 161 | 162 | 163 | =back 164 | 165 | =head1 EXIT STATUS 166 | 167 | The asignify return zero exit code on success, and non-zero if an error occurs. 168 | It may fail because of one of the following reasons: 169 | 170 | =over 4 171 | 172 | =item - Some files requested are absent. 173 | 174 | =item - Passphrase is incorrect (or passphrase and verification are not equal). 175 | 176 | =item - The message file has been corrupted and its signature is no longer valid. 177 | 178 | =back 179 | 180 | =head1 EXAMPLES 181 | 182 | F 183 | 184 | $ asignify generate keys/key.secret keys/key.public 185 | 186 | F 187 | 188 | $ asignify sign -d blake2 keys/key.secret motd.sig /etc/motd 189 | 190 | F 191 | 192 | $ asignify verify keys/key.public motd.sig 193 | 194 | F 195 | 196 | $ asignify check keys/key.public motd.sig /etc/motd 197 | 198 | 199 | -------------------------------------------------------------------------------- /include/Makefile.am: -------------------------------------------------------------------------------- 1 | include_HEADERS = asignify.h 2 | -------------------------------------------------------------------------------- /include/asignify.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2015, Vsevolod Stakhov 2 | * All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are met: 6 | * * Redistributions of source code must retain the above copyright 7 | * notice, this list of conditions and the following disclaimer. 8 | * * Redistributions in binary form must reproduce the above copyright 9 | * notice, this list of conditions and the following disclaimer in the 10 | * documentation and/or other materials provided with the distribution. 11 | * 12 | * THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY 13 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 14 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 15 | * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY 16 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 17 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 18 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 19 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 20 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 21 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 22 | */ 23 | 24 | #ifndef libasignify_H 25 | #define libasignify_H 26 | 27 | #include 28 | #include 29 | #include 30 | 31 | #define PBKDF_MINROUNDS 10000 32 | 33 | #if defined(__cplusplus) 34 | extern "C" { 35 | #endif 36 | 37 | /* 38 | * Opaque structures 39 | */ 40 | struct asignify_verify_ctx; 41 | struct asignify_sign_ctx; 42 | struct asignify_encrypt_ctx; 43 | typedef struct asignify_verify_ctx asignify_verify_t; 44 | typedef struct asignify_sign_ctx asignify_sign_t; 45 | typedef struct asignify_encrypt_ctx asignify_encrypt_t; 46 | 47 | typedef int (*asignify_password_cb)(char *buf, size_t len, void *d); 48 | 49 | /** 50 | * Signature type 51 | */ 52 | enum asignify_digest_type { 53 | ASIGNIFY_DIGEST_SHA256 = 0, 54 | ASIGNIFY_DIGEST_SHA512, 55 | ASIGNIFY_DIGEST_BLAKE2, 56 | ASIGNIFY_DIGEST_SIZE, 57 | ASIGNIFY_DIGEST_MAX 58 | }; 59 | 60 | /** 61 | * Encryption type 62 | */ 63 | enum asignify_encrypt_type { 64 | ASIGNIFY_ENCRYPT_SAFE = 0, 65 | ASIGNIFY_ENCRYPT_FAST 66 | }; 67 | 68 | /** 69 | * Initialize verify context 70 | * @return new verify context or NULL 71 | */ 72 | asignify_verify_t* asignify_verify_init(void); 73 | 74 | /** 75 | * Load public key from a file 76 | * @param ctx verify context 77 | * @param pubf file name or '-' to read from stdin 78 | * @return true if a key has been successfully loaded 79 | */ 80 | bool asignify_verify_load_pubkey(asignify_verify_t *ctx, const char *pubf); 81 | 82 | /** 83 | * Load and parse signature file 84 | * @param ctx verify context 85 | * @param sigf file name or '-' to read from stdin 86 | * @return true if a signature has been successfully loaded 87 | */ 88 | bool asignify_verify_load_signature(asignify_verify_t *ctx, const char *sigf); 89 | 90 | /** 91 | * Verify file against parsed signature and pubkey 92 | * @param ctx verify context 93 | * @param checkf file name or '-' to read from stdin 94 | * @return true if a file is valid 95 | */ 96 | bool asignify_verify_file(asignify_verify_t *ctx, const char *checkf); 97 | 98 | /** 99 | * Returns last error for verify context 100 | * @param ctx verify context 101 | * @return constant string corresponding to the last error occurred during verification 102 | */ 103 | const char* asignify_verify_get_error(asignify_verify_t *ctx); 104 | 105 | /** 106 | * Free verify context 107 | * @param ctx verify context 108 | */ 109 | void asignify_verify_free(asignify_verify_t *ctx); 110 | 111 | /** 112 | * Initialize sign context 113 | * @return new sign context or NULL 114 | */ 115 | asignify_sign_t* asignify_sign_init(void); 116 | 117 | /** 118 | * Load private key from a file 119 | * @param ctx sign context 120 | * @param privf file name or '-' to read from stdin 121 | * @param password_cb function that is called to get password from a user 122 | * @param d opaque data pointer for password callback 123 | * @return true if a key has been successfully loaded 124 | */ 125 | bool asignify_sign_load_privkey(asignify_sign_t *ctx, const char *privf, 126 | asignify_password_cb password_cb, void *d); 127 | 128 | /** 129 | * Add specified file to the signature context 130 | * @param ctx sign context 131 | * @param f file name or '-' to read from stdin 132 | * @param dt type of digest to be calculated 133 | * @return true if a file is valid 134 | */ 135 | bool asignify_sign_add_file(asignify_sign_t *ctx, const char *f, 136 | enum asignify_digest_type dt); 137 | 138 | /** 139 | * Write the complete signature for this context 140 | * @param ctx sign context 141 | * @param sigf file name or '-' to write to stdout 142 | * @return true if a signature has been successfully written 143 | */ 144 | bool asignify_sign_write_signature(asignify_sign_t *ctx, const char *sigf); 145 | 146 | /** 147 | * Returns last error for sign context 148 | * @param ctx sign context 149 | * @return constant string corresponding to the last error occurred during signing 150 | */ 151 | const char* asignify_sign_get_error(asignify_sign_t *ctx); 152 | 153 | /** 154 | * Free sign context 155 | * @param ctx sign context 156 | */ 157 | void asignify_sign_free(asignify_sign_t *ctx); 158 | 159 | /** 160 | * Generate new keypair 161 | * @param privkf filename for private key 162 | * @param pubkf filename for public key 163 | * @param version version of pair 164 | * @param rounds rounds of PBKDF (if 0 then private key is not encrypted) 165 | * @param password_cb password callback (if NULL then private key is not encrypted) 166 | * @param d opaque data pointer for password 167 | * @return true if pair has been written successfully 168 | */ 169 | bool asignify_generate(const char *privkf, const char *pubkf, 170 | unsigned int version, unsigned int rounds, 171 | asignify_password_cb password_cb, void *d); 172 | 173 | /** 174 | * Regenerate public key for given private key 175 | * @param privkf filename for private key 176 | * @param pubkf filename for public key 177 | * @param password_cb password callback 178 | * @param d opaque data pointer for password 179 | * @return true if pair has been written successfully 180 | */ 181 | bool asignify_generate_pubkey(const char *privkf, const char *pubkf, 182 | asignify_password_cb password_cb, void *d); 183 | 184 | /** 185 | * Safely zero specified memory region 186 | * @param pnt pointer to zero 187 | * @param len size of region 188 | */ 189 | void explicit_memzero(void * const pnt, const size_t len); 190 | 191 | /** 192 | * Returns size of specified digest 193 | * @param type type of digest 194 | * @return size of digest or 0 if it is invalid 195 | */ 196 | unsigned int asignify_digest_len(enum asignify_digest_type type); 197 | 198 | /** 199 | * Get name of the specified digest 200 | * @param type type of digest 201 | * @return symbolic name or NULL, this string must not be modified 202 | */ 203 | const char * asignify_digest_name(enum asignify_digest_type type); 204 | 205 | /** 206 | * Calculates specific digest for a file represented by an open fd 207 | * @param type type of digest 208 | * @param fd file descriptor 209 | * @return allocated binary chunk with the digest or NULL in case of failure (this blob must be freed after use) 210 | */ 211 | unsigned char* asignify_digest_fd(enum asignify_digest_type type, int fd); 212 | 213 | /** 214 | * Parse string and returns the digest type 215 | * @param data string to parse 216 | * @param dlen size of string 217 | * @return valid digest id or ASIGNIFY_DIGEST_MAX in case of failure 218 | */ 219 | enum asignify_digest_type asignify_digest_from_str(const char *data, 220 | ssize_t dlen); 221 | 222 | /** 223 | * Convert unencrypted SSH ed25519 private key to the native format 224 | * @param sshkf filename for ssh key 225 | * @param privkf filename for native key 226 | * @param version veriosn to use (1 is the current version) 227 | * @param rounds rounds to apply PBKDF 228 | * @param password_cb password callback (or NULL for unencrypted native key) 229 | * @param d opaque data pointer for password callback 230 | * @return true if key has been generated successfully 231 | */ 232 | bool asignify_privkey_from_ssh(const char *sshkf, const char *privkf, 233 | unsigned int version, unsigned int rounds, 234 | asignify_password_cb password_cb, void *d); 235 | 236 | /** 237 | * Initialize encrypt context 238 | * @return new encrypt context or NULL 239 | */ 240 | asignify_encrypt_t* asignify_encrypt_init(void); 241 | 242 | /** 243 | * Load public key from a file 244 | * @param ctx encrypt context 245 | * @param pubf file name or '-' to read from stdin 246 | * @return true if a key has been successfully loaded 247 | */ 248 | bool asignify_encrypt_load_pubkey(asignify_encrypt_t *ctx, const char *pubf); 249 | 250 | /** 251 | * Load private key from a file 252 | * @param ctx encrypt context 253 | * @param privf file name or '-' to read from stdin 254 | * @param password_cb function that is called to get password from a user 255 | * @param d opaque data pointer for password callback 256 | * @return true if a key has been successfully loaded 257 | */ 258 | bool asignify_encrypt_load_privkey(asignify_encrypt_t *ctx, const char *privf, 259 | asignify_password_cb password_cb, void *d); 260 | 261 | /** 262 | * Encrypt and sign the specified file using remote pubkey and local privkey 263 | * @param ctx encrypt context 264 | * @param version version of encryption 265 | * @param inf input file 266 | * @param outf output file (MUST be a regular file) 267 | * @return true if input has been encrypted and signed 268 | */ 269 | bool 270 | asignify_encrypt_crypt_file(asignify_encrypt_t *ctx, unsigned int version, 271 | const char *inf, const char *outf, enum asignify_encrypt_type type); 272 | 273 | /** 274 | * Validate and decrypt the specified file using remote pubkey and local privkey 275 | * @param ctx encrypt context 276 | * @param inf input file (MUST be a regular file) 277 | * @param outf output file 278 | * @return true if input has been verified and decrypted 279 | */ 280 | bool 281 | asignify_encrypt_decrypt_file(asignify_encrypt_t *ctx, const char *inf, 282 | const char *outf); 283 | /** 284 | * Returns last error for encrypt context 285 | * @param ctx encrypt context 286 | * @return constant string corresponding to the last error occurred during signing 287 | */ 288 | const char* asignify_encrypt_get_error(asignify_encrypt_t *ctx); 289 | 290 | /** 291 | * Free encrypt context 292 | * @param ctx encrypt context 293 | */ 294 | void asignify_encrypt_free(asignify_encrypt_t *ctx); 295 | 296 | #if defined(__cplusplus) 297 | } 298 | #endif 299 | 300 | #endif 301 | -------------------------------------------------------------------------------- /libasignify/Makefile.am: -------------------------------------------------------------------------------- 1 | lib_LTLIBRARIES = libasignify.la 2 | 3 | noinst_HEADERS= khash.h \ 4 | kvec.h \ 5 | tweetnacl.h \ 6 | blake2.h \ 7 | blake2-impl.h \ 8 | sha2.h \ 9 | chacha.h \ 10 | asignify_internal.h 11 | 12 | # Sources for libasignify 13 | libasignify_la_SOURCES = tweetnacl.c \ 14 | blake2b-ref.c \ 15 | chacha.c \ 16 | sha2.c \ 17 | pbkdf2.c \ 18 | b64_pton.c \ 19 | databuf.c \ 20 | generate.c \ 21 | pubkey.c \ 22 | verify.c \ 23 | sign.c \ 24 | signature.c \ 25 | encrypt.c \ 26 | util.c 27 | 28 | libasignify_la_LDFLAGS = -version-info @ASIGNIFY_LIBRARY_VERSION@ \ 29 | @OPENSSL_LDFLAGS@ \ 30 | @OPENSSL_LIBS@ \ 31 | @OS_LIBS@ 32 | 33 | libasignify_la_CPPFLAGS = -I$(top_srcdir)/include \ 34 | @OS_CFLAGS@ \ 35 | @OPENSSL_INCLUDES@ -------------------------------------------------------------------------------- /libasignify/asignify_internal.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2015, Vsevolod Stakhov 2 | * All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are met: 6 | * * Redistributions of source code must retain the above copyright 7 | * notice, this list of conditions and the following disclaimer. 8 | * * Redistributions in binary form must reproduce the above copyright 9 | * notice, this list of conditions and the following disclaimer in the 10 | * documentation and/or other materials provided with the distribution. 11 | * 12 | * THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY 13 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 14 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 15 | * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY 16 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 17 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 18 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 19 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 20 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 21 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 22 | */ 23 | #ifndef ASIGNIFY_INTERNAL_H_ 24 | #define ASIGNIFY_INTERNAL_H_ 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include /* for mode_t */ 31 | 32 | #include "asignify.h" 33 | 34 | #define OBSD_COMMENTHDR "untrusted comment: " 35 | #define PRIVKEY_MAGIC "asignify-private-key" 36 | #define KEY_ID_LEN 8 37 | #define SALT_LEN 16 38 | #define PBKDF_ALG "pbkdf2-blake2" 39 | 40 | #if defined(__GNUC__) && __GNUC__ >= 4 41 | #define STRUCT_OFFSET(struct_type, member) \ 42 | ((long) offsetof(struct_type, member)) 43 | #else 44 | #define STRUCT_OFFSET(struct_type, member) \ 45 | ((long)((unsigned char*) &((struct_type*) 0)->member)) 46 | #endif 47 | #define STRUCT_MEMBER_PTR(member_type, struct_p, struct_offset) \ 48 | ((member_type*)((void *)((unsigned char*)(struct_p) + (long)(struct_offset)))) 49 | 50 | #ifndef nitems 51 | #define nitems(x) (sizeof((x)) / sizeof((x)[0])) 52 | #endif 53 | 54 | struct asignify_public_data { 55 | unsigned char *data; 56 | size_t data_len; 57 | unsigned char *id; 58 | size_t id_len; 59 | unsigned char *aux; 60 | size_t aux_len; 61 | unsigned int version; 62 | }; 63 | 64 | struct asignify_private_data { 65 | unsigned char *data; 66 | size_t data_len; 67 | unsigned char *id; 68 | size_t id_len; 69 | unsigned int version; 70 | }; 71 | 72 | struct asignify_private_key { 73 | unsigned int version; 74 | char *pbkdf_alg; 75 | unsigned int rounds; 76 | unsigned char *salt; 77 | unsigned char *checksum; 78 | unsigned char *id; 79 | unsigned char *encrypted_blob; 80 | }; 81 | 82 | struct asignify_file_digest { 83 | enum asignify_digest_type digest_type; 84 | unsigned char *digest; 85 | struct asignify_file_digest *next; 86 | }; 87 | 88 | struct asignify_file { 89 | char *fname; 90 | struct asignify_file_digest *digests; 91 | size_t size; 92 | }; 93 | 94 | void randombytes(unsigned char *buf, uint64_t len); 95 | 96 | int pkcs5_pbkdf2(const char *pass, size_t pass_len, const uint8_t *salt, 97 | size_t salt_len, uint8_t *key, size_t key_len, unsigned int rounds); 98 | 99 | FILE * xfopen(const char *fname, const char *mode); 100 | int xopen(const char *fname, int oflags, mode_t mode); 101 | void * xmalloc(size_t len); 102 | void * xmalloc_aligned(size_t align, size_t len); 103 | void * xmalloc0(size_t len); 104 | char * xstrdup(const char *str); 105 | 106 | int b64_pton(char const *src, unsigned char *target, size_t targsize); 107 | int b64_pton_stop(char const *src, unsigned char *target, size_t targsize, const char *stop); 108 | int b64_ntop(unsigned char *src, size_t srclength, char *target, 109 | size_t targsize); 110 | 111 | int hex2bin(unsigned char * const bin, const size_t bin_maxlen, 112 | const char * const hex, const size_t hex_len, 113 | size_t * const bin_len, const char ** const hex_end); 114 | char * bin2hex(char * const hex, const size_t hex_maxlen, 115 | const unsigned char * const bin, const size_t bin_len); 116 | 117 | enum asignify_error { 118 | ASIGNIFY_ERROR_OK = 0, 119 | ASIGNIFY_ERROR_NO_PUBKEY, 120 | ASIGNIFY_ERROR_FILE, 121 | ASIGNIFY_ERROR_FORMAT, 122 | ASIGNIFY_ERROR_DECRYPT, 123 | ASIGNIFY_ERROR_PASSWORD, 124 | ASIGNIFY_ERROR_VERIFY, 125 | ASIGNIFY_ERROR_SIZE, 126 | ASIGNIFY_ERROR_VERIFY_SIZE, 127 | ASIGNIFY_ERROR_VERIFY_DIGEST, 128 | ASIGNIFY_ERROR_NO_DIGEST, 129 | ASIGNIFY_ERROR_MISUSE, 130 | ASIGNIFY_ERROR_WRONG_KEYPAIR, 131 | ASIGNIFY_ERROR_WRONG_KEY, 132 | ASIGNIFY_ERROR_MAX 133 | }; 134 | 135 | #define CTX_MAYBE_SET_ERR(ctx, err) do { \ 136 | if ((ctx) != NULL) { \ 137 | (ctx)->error = xerr_string((err)); \ 138 | } \ 139 | } while(0) 140 | 141 | const char * xerr_string(enum asignify_error code); 142 | 143 | /* 144 | * Common public data operations 145 | */ 146 | void asignify_alloc_public_data_fields(struct asignify_public_data *pk); 147 | struct asignify_public_data* asignify_public_data_load(const char *buf, 148 | size_t buflen, const char *magic, 149 | size_t magiclen, unsigned int ver_min, unsigned int ver_max, 150 | unsigned int id_len, unsigned int data_len); 151 | void asignify_public_data_free(struct asignify_public_data *d); 152 | 153 | /* 154 | * Common secret data operations 155 | */ 156 | struct asignify_private_data* asignify_private_data_load(FILE *f, int *error, 157 | asignify_password_cb password_cb, void *d); 158 | void asignify_private_data_free(struct asignify_private_data *d); 159 | bool asignify_privkey_write(struct asignify_private_key *privk, FILE *f); 160 | struct asignify_public_data* asignify_private_data_sign( 161 | struct asignify_private_data *privk, unsigned char *buf, size_t len); 162 | 163 | /* 164 | * Pubkey operations 165 | */ 166 | struct asignify_public_data* asignify_pubkey_load(FILE *f); 167 | bool asignify_pubkey_check_signature(struct asignify_public_data *pk, 168 | struct asignify_public_data *sig, const unsigned char *data, size_t dlen); 169 | bool asignify_pubkey_write(struct asignify_public_data *pk, FILE *f); 170 | 171 | /* 172 | * Signature operations 173 | */ 174 | struct asignify_public_data* asignify_signature_load(FILE *f, 175 | struct asignify_public_data *pk); 176 | bool asignify_signature_write(struct asignify_public_data *sig, const void *buf, 177 | size_t len, FILE *f); 178 | 179 | /* 180 | * SSH keys routines 181 | */ 182 | const unsigned char * asignify_ssh_read_string(const unsigned char *buf, 183 | unsigned int *str_len, unsigned int remain, unsigned char const **npos); 184 | struct asignify_private_data* asignify_ssh_privkey_load(FILE *f, int *error); 185 | 186 | #endif /* ASIGNIFY_INTERNAL_H_ */ 187 | -------------------------------------------------------------------------------- /libasignify/b64_pton.c: -------------------------------------------------------------------------------- 1 | /* $OpenBSD: base64.c,v 1.7 2013/12/31 02:32:56 tedu Exp $ */ 2 | 3 | /* 4 | * Copyright (c) 1996 by Internet Software Consortium. 5 | * 6 | * Permission to use, copy, modify, and distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS 11 | * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES 12 | * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE 13 | * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL 14 | * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR 15 | * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS 16 | * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 17 | * SOFTWARE. 18 | */ 19 | 20 | /* 21 | * Portions Copyright (c) 1995 by International Business Machines, Inc. 22 | * 23 | * International Business Machines, Inc. (hereinafter called IBM) grants 24 | * permission under its copyrights to use, copy, modify, and distribute this 25 | * Software with or without fee, provided that the above copyright notice and 26 | * all paragraphs of this notice appear in all copies, and that the name of IBM 27 | * not be used in connection with the marketing of any product incorporating 28 | * the Software or modifications thereof, without specific, written prior 29 | * permission. 30 | * 31 | * To the extent it has a right to do so, IBM grants an immunity from suit 32 | * under its patents, if any, for the use, sale or manufacture of products to 33 | * the extent that such products are used for performing Domain Name System 34 | * dynamic updates in TCP/IP networks by means of the Software. No immunity is 35 | * granted for any product per se or for any other function of any product. 36 | * 37 | * THE SOFTWARE IS PROVIDED "AS IS", AND IBM DISCLAIMS ALL WARRANTIES, 38 | * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 39 | * PARTICULAR PURPOSE. IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL, 40 | * DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER ARISING 41 | * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE, EVEN 42 | * IF IBM IS APPRISED OF THE POSSIBILITY OF SUCH DAMAGES. 43 | */ 44 | 45 | #include 46 | #include 47 | 48 | #include 49 | #include 50 | 51 | static const char Base64[] = 52 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 53 | static const char Pad64 = '='; 54 | 55 | /* (From RFC1521 and draft-ietf-dnssec-secext-03.txt) 56 | The following encoding technique is taken from RFC 1521 by Borenstein 57 | and Freed. It is reproduced here in a slightly edited form for 58 | convenience. 59 | 60 | A 65-character subset of US-ASCII is used, enabling 6 bits to be 61 | represented per printable character. (The extra 65th character, "=", 62 | is used to signify a special processing function.) 63 | 64 | The encoding process represents 24-bit groups of input bits as output 65 | strings of 4 encoded characters. Proceeding from left to right, a 66 | 24-bit input group is formed by concatenating 3 8-bit input groups. 67 | These 24 bits are then treated as 4 concatenated 6-bit groups, each 68 | of which is translated into a single digit in the base64 alphabet. 69 | 70 | Each 6-bit group is used as an index into an array of 64 printable 71 | characters. The character referenced by the index is placed in the 72 | output string. 73 | 74 | Table 1: The Base64 Alphabet 75 | 76 | Value Encoding Value Encoding Value Encoding Value Encoding 77 | 0 A 17 R 34 i 51 z 78 | 1 B 18 S 35 j 52 0 79 | 2 C 19 T 36 k 53 1 80 | 3 D 20 U 37 l 54 2 81 | 4 E 21 V 38 m 55 3 82 | 5 F 22 W 39 n 56 4 83 | 6 G 23 X 40 o 57 5 84 | 7 H 24 Y 41 p 58 6 85 | 8 I 25 Z 42 q 59 7 86 | 9 J 26 a 43 r 60 8 87 | 10 K 27 b 44 s 61 9 88 | 11 L 28 c 45 t 62 + 89 | 12 M 29 d 46 u 63 / 90 | 13 N 30 e 47 v 91 | 14 O 31 f 48 w (pad) = 92 | 15 P 32 g 49 x 93 | 16 Q 33 h 50 y 94 | 95 | Special processing is performed if fewer than 24 bits are available 96 | at the end of the data being encoded. A full encoding quantum is 97 | always completed at the end of a quantity. When fewer than 24 input 98 | bits are available in an input group, zero bits are added (on the 99 | right) to form an integral number of 6-bit groups. Padding at the 100 | end of the data is performed using the '=' character. 101 | 102 | Since all base64 input is an integral number of octets, only the 103 | ------------------------------------------------- 104 | following cases can arise: 105 | 106 | (1) the final quantum of encoding input is an integral 107 | multiple of 24 bits; here, the final unit of encoded 108 | output will be an integral multiple of 4 characters 109 | with no "=" padding, 110 | (2) the final quantum of encoding input is exactly 8 bits; 111 | here, the final unit of encoded output will be two 112 | characters followed by two "=" padding characters, or 113 | (3) the final quantum of encoding input is exactly 16 bits; 114 | here, the final unit of encoded output will be three 115 | characters followed by one "=" padding character. 116 | */ 117 | 118 | int 119 | b64_ntop(unsigned char *src, size_t srclength, char *target, size_t targsize) 120 | { 121 | size_t datalength = 0; 122 | unsigned char input[3]; 123 | unsigned char output[4]; 124 | int i; 125 | 126 | while (2 < srclength) { 127 | input[0] = *src++; 128 | input[1] = *src++; 129 | input[2] = *src++; 130 | srclength -= 3; 131 | 132 | output[0] = input[0] >> 2; 133 | output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4); 134 | output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6); 135 | output[3] = input[2] & 0x3f; 136 | 137 | if (datalength + 4 > targsize) 138 | return (-1); 139 | target[datalength++] = Base64[output[0]]; 140 | target[datalength++] = Base64[output[1]]; 141 | target[datalength++] = Base64[output[2]]; 142 | target[datalength++] = Base64[output[3]]; 143 | } 144 | 145 | /* Now we worry about padding. */ 146 | if (0 != srclength) { 147 | /* Get what's left. */ 148 | input[0] = input[1] = input[2] = '\0'; 149 | for (i = 0; i < srclength; i++) 150 | input[i] = *src++; 151 | 152 | output[0] = input[0] >> 2; 153 | output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4); 154 | output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6); 155 | 156 | if (datalength + 4 > targsize) 157 | return (-1); 158 | target[datalength++] = Base64[output[0]]; 159 | target[datalength++] = Base64[output[1]]; 160 | if (srclength == 1) 161 | target[datalength++] = Pad64; 162 | else 163 | target[datalength++] = Base64[output[2]]; 164 | target[datalength++] = Pad64; 165 | } 166 | if (datalength >= targsize) 167 | return (-1); 168 | target[datalength] = '\0'; /* Returned value doesn't count \0. */ 169 | return (datalength); 170 | } 171 | 172 | /* skips all whitespace anywhere. 173 | converts characters, four at a time, starting at (or after) 174 | src from base - 64 numbers into three 8 bit bytes in the target area. 175 | it returns the number of data bytes stored at the target, or -1 on error. 176 | */ 177 | 178 | int 179 | b64_pton_stop(char const *src, unsigned char *target, size_t targsize, 180 | const char *stop) 181 | { 182 | int tarindex, state, ch, slen; 183 | unsigned char nextbyte; 184 | char *pos; 185 | 186 | state = 0; 187 | tarindex = 0; 188 | slen = strlen(stop) + 1; 189 | 190 | for (;;) { 191 | ch = (unsigned char)*src++; 192 | 193 | if (memchr(stop, ch, slen) != NULL) 194 | break; 195 | 196 | if (isspace(ch)) /* Skip whitespace anywhere. */ 197 | continue; 198 | 199 | if (ch == Pad64) 200 | break; 201 | 202 | pos = strchr(Base64, ch); 203 | if (pos == 0) /* A non-base64 character. */ 204 | return (-1); 205 | 206 | switch (state) { 207 | case 0: 208 | if (target) { 209 | if (tarindex >= targsize) 210 | return (-1); 211 | target[tarindex] = (pos - Base64) << 2; 212 | } 213 | state = 1; 214 | break; 215 | case 1: 216 | if (target) { 217 | if (tarindex >= targsize) 218 | return (-1); 219 | target[tarindex] |= (pos - Base64) >> 4; 220 | nextbyte = ((pos - Base64) & 0x0f) << 4; 221 | if (tarindex + 1 < targsize) 222 | target[tarindex+1] = nextbyte; 223 | else if (nextbyte) 224 | return (-1); 225 | } 226 | tarindex++; 227 | state = 2; 228 | break; 229 | case 2: 230 | if (target) { 231 | if (tarindex >= targsize) 232 | return (-1); 233 | target[tarindex] |= (pos - Base64) >> 2; 234 | nextbyte = ((pos - Base64) & 0x03) << 6; 235 | if (tarindex + 1 < targsize) 236 | target[tarindex+1] = nextbyte; 237 | else if (nextbyte) 238 | return (-1); 239 | } 240 | tarindex++; 241 | state = 3; 242 | break; 243 | case 3: 244 | if (target) { 245 | if (tarindex >= targsize) 246 | return (-1); 247 | target[tarindex] |= (pos - Base64); 248 | } 249 | tarindex++; 250 | state = 0; 251 | break; 252 | } 253 | } 254 | 255 | /* 256 | * We are done decoding Base-64 chars. Let's see if we ended 257 | * on a byte boundary, and/or with erroneous trailing characters. 258 | */ 259 | 260 | if (ch == Pad64) { /* We got a pad char. */ 261 | ch = (unsigned char)*src++; /* Skip it, get next. */ 262 | switch (state) { 263 | case 0: /* Invalid = in first position */ 264 | case 1: /* Invalid = in second position */ 265 | return (-1); 266 | 267 | case 2: /* Valid, means one byte of info */ 268 | /* Skip any number of spaces. */ 269 | for (; ch != '\0'; ch = (unsigned char)*src++) 270 | if (!isspace(ch)) 271 | break; 272 | /* Make sure there is another trailing = sign. */ 273 | if (ch != Pad64) 274 | return (-1); 275 | ch = (unsigned char)*src++; /* Skip the = */ 276 | /* Fall through to "single trailing =" case. */ 277 | /* FALLTHROUGH */ 278 | 279 | case 3: /* Valid, means two bytes of info */ 280 | /* 281 | * We know this char is an =. Is there anything but 282 | * whitespace after it? 283 | */ 284 | for (;; ch = (unsigned char)*src++) { 285 | if (memchr(stop, ch, slen) != NULL) { 286 | break; 287 | } 288 | else if (!isspace(ch)) { 289 | return (-1); 290 | } 291 | } 292 | 293 | /* 294 | * Now make sure for cases 2 and 3 that the "extra" 295 | * bits that slopped past the last full byte were 296 | * zeros. If we don't check them, they become a 297 | * subliminal channel. 298 | */ 299 | if (target && tarindex < targsize && 300 | target[tarindex] != 0) 301 | return (-1); 302 | } 303 | } else { 304 | /* 305 | * We ended by seeing the end of the string. Make sure we 306 | * have no partial bytes lying around. 307 | */ 308 | if (state != 0) 309 | return (-1); 310 | } 311 | 312 | return (tarindex); 313 | } 314 | 315 | int 316 | b64_pton(char const *src, unsigned char *target, size_t targsize) 317 | { 318 | return (b64_pton_stop(src, target, targsize, "")); 319 | } 320 | -------------------------------------------------------------------------------- /libasignify/blake2-impl.h: -------------------------------------------------------------------------------- 1 | /* 2 | BLAKE2 reference source code package - reference C implementations 3 | 4 | Written in 2012 by Samuel Neves 5 | 6 | To the extent possible under law, the author(s) have dedicated all copyright 7 | and related and neighboring rights to this software to the public domain 8 | worldwide. This software is distributed without any warranty. 9 | 10 | You should have received a copy of the CC0 Public Domain Dedication along with 11 | this software. If not, see . 12 | */ 13 | #pragma once 14 | #ifndef __BLAKE2_IMPL_H__ 15 | #define __BLAKE2_IMPL_H__ 16 | 17 | #ifdef HAVE_CONFIG_H 18 | #include "config.h" 19 | #endif 20 | 21 | #include 22 | 23 | #ifdef HAVE_SYS_ENDIAN_H 24 | #include 25 | #elif HAVE_ENDIAN_H 26 | #include 27 | #elif HAVE_MACHINE_ENDIAN_H 28 | #include 29 | #endif 30 | 31 | #ifdef __BYTE_ORDER 32 | # if __BYTE_ORDER == __LITTLE_ENDIAN 33 | # define NATIVE_LITTLE_ENDIAN 34 | # endif 35 | #endif 36 | 37 | static inline uint32_t load32( const void *src ) 38 | { 39 | #if defined(NATIVE_LITTLE_ENDIAN) 40 | uint32_t w; 41 | memcpy(&w, src, sizeof w); 42 | return w; 43 | #else 44 | const uint8_t *p = ( const uint8_t * )src; 45 | uint32_t w = *p++; 46 | w |= ( uint32_t )( *p++ ) << 8; 47 | w |= ( uint32_t )( *p++ ) << 16; 48 | w |= ( uint32_t )( *p++ ) << 24; 49 | return w; 50 | #endif 51 | } 52 | 53 | static inline uint64_t load64( const void *src ) 54 | { 55 | #if defined(NATIVE_LITTLE_ENDIAN) 56 | uint64_t w; 57 | memcpy(&w, src, sizeof w); 58 | return w; 59 | #else 60 | const uint8_t *p = ( const uint8_t * )src; 61 | uint64_t w = *p++; 62 | w |= ( uint64_t )( *p++ ) << 8; 63 | w |= ( uint64_t )( *p++ ) << 16; 64 | w |= ( uint64_t )( *p++ ) << 24; 65 | w |= ( uint64_t )( *p++ ) << 32; 66 | w |= ( uint64_t )( *p++ ) << 40; 67 | w |= ( uint64_t )( *p++ ) << 48; 68 | w |= ( uint64_t )( *p++ ) << 56; 69 | return w; 70 | #endif 71 | } 72 | 73 | static inline void store32( void *dst, uint32_t w ) 74 | { 75 | #if defined(NATIVE_LITTLE_ENDIAN) 76 | memcpy(dst, &w, sizeof w); 77 | #else 78 | uint8_t *p = ( uint8_t * )dst; 79 | *p++ = ( uint8_t )w; w >>= 8; 80 | *p++ = ( uint8_t )w; w >>= 8; 81 | *p++ = ( uint8_t )w; w >>= 8; 82 | *p++ = ( uint8_t )w; 83 | #endif 84 | } 85 | 86 | static inline void store64( void *dst, uint64_t w ) 87 | { 88 | #if defined(NATIVE_LITTLE_ENDIAN) 89 | memcpy(dst, &w, sizeof w); 90 | #else 91 | uint8_t *p = ( uint8_t * )dst; 92 | *p++ = ( uint8_t )w; w >>= 8; 93 | *p++ = ( uint8_t )w; w >>= 8; 94 | *p++ = ( uint8_t )w; w >>= 8; 95 | *p++ = ( uint8_t )w; w >>= 8; 96 | *p++ = ( uint8_t )w; w >>= 8; 97 | *p++ = ( uint8_t )w; w >>= 8; 98 | *p++ = ( uint8_t )w; w >>= 8; 99 | *p++ = ( uint8_t )w; 100 | #endif 101 | } 102 | 103 | static inline uint64_t load48( const void *src ) 104 | { 105 | const uint8_t *p = ( const uint8_t * )src; 106 | uint64_t w = *p++; 107 | w |= ( uint64_t )( *p++ ) << 8; 108 | w |= ( uint64_t )( *p++ ) << 16; 109 | w |= ( uint64_t )( *p++ ) << 24; 110 | w |= ( uint64_t )( *p++ ) << 32; 111 | w |= ( uint64_t )( *p++ ) << 40; 112 | return w; 113 | } 114 | 115 | static inline void store48( void *dst, uint64_t w ) 116 | { 117 | uint8_t *p = ( uint8_t * )dst; 118 | *p++ = ( uint8_t )w; w >>= 8; 119 | *p++ = ( uint8_t )w; w >>= 8; 120 | *p++ = ( uint8_t )w; w >>= 8; 121 | *p++ = ( uint8_t )w; w >>= 8; 122 | *p++ = ( uint8_t )w; w >>= 8; 123 | *p++ = ( uint8_t )w; 124 | } 125 | 126 | static inline uint32_t rotl32( const uint32_t w, const unsigned c ) 127 | { 128 | return ( w << c ) | ( w >> ( 32 - c ) ); 129 | } 130 | 131 | static inline uint64_t rotl64( const uint64_t w, const unsigned c ) 132 | { 133 | return ( w << c ) | ( w >> ( 64 - c ) ); 134 | } 135 | 136 | static inline uint32_t rotr32( const uint32_t w, const unsigned c ) 137 | { 138 | return ( w >> c ) | ( w << ( 32 - c ) ); 139 | } 140 | 141 | static inline uint64_t rotr64( const uint64_t w, const unsigned c ) 142 | { 143 | return ( w >> c ) | ( w << ( 64 - c ) ); 144 | } 145 | 146 | /* prevents compiler optimizing out memset() */ 147 | static inline void secure_zero_memory( void *v, size_t n ) 148 | { 149 | volatile uint8_t *p = ( volatile uint8_t * )v; 150 | while( n-- ) *p++ = 0; 151 | } 152 | 153 | #endif 154 | 155 | -------------------------------------------------------------------------------- /libasignify/blake2.h: -------------------------------------------------------------------------------- 1 | /* 2 | BLAKE2 reference source code package - reference C implementations 3 | 4 | Written in 2012 by Samuel Neves 5 | 6 | To the extent possible under law, the author(s) have dedicated all copyright 7 | and related and neighboring rights to this software to the public domain 8 | worldwide. This software is distributed without any warranty. 9 | 10 | You should have received a copy of the CC0 Public Domain Dedication along with 11 | this software. If not, see . 12 | */ 13 | #pragma once 14 | #ifndef __BLAKE2_H__ 15 | #define __BLAKE2_H__ 16 | 17 | #include 18 | #include 19 | 20 | #ifndef BLAKE_ALIGN 21 | # if defined(_MSC_VER) 22 | # define BLAKE_ALIGN(x) __declspec(align(x)) 23 | # else 24 | # define BLAKE_ALIGN(x) __attribute__((aligned(x))) 25 | # endif 26 | #endif 27 | 28 | #if defined(__cplusplus) 29 | extern "C" { 30 | #endif 31 | 32 | 33 | enum blake2b_constant 34 | { 35 | BLAKE2B_BLOCKBYTES = 128, 36 | BLAKE2B_OUTBYTES = 64, 37 | BLAKE2B_KEYBYTES = 64, 38 | BLAKE2B_SALTBYTES = 16, 39 | BLAKE2B_PERSONALBYTES = 16 40 | }; 41 | 42 | #pragma pack(push, 1) 43 | 44 | typedef struct __blake2b_param 45 | { 46 | uint8_t digest_length; // 1 47 | uint8_t key_length; // 2 48 | uint8_t fanout; // 3 49 | uint8_t depth; // 4 50 | uint32_t leaf_length; // 8 51 | uint64_t node_offset; // 16 52 | uint8_t node_depth; // 17 53 | uint8_t inner_length; // 18 54 | uint8_t reserved[14]; // 32 55 | uint8_t salt[BLAKE2B_SALTBYTES]; // 48 56 | uint8_t personal[BLAKE2B_PERSONALBYTES]; // 64 57 | } blake2b_param; 58 | 59 | typedef struct BLAKE_ALIGN( 64 ) __blake2b_state 60 | { 61 | uint64_t h[8]; 62 | uint64_t t[2]; 63 | uint64_t f[2]; 64 | uint8_t buf[2 * BLAKE2B_BLOCKBYTES]; 65 | size_t buflen; 66 | uint8_t last_node; 67 | } blake2b_state; 68 | 69 | #pragma pack(pop) 70 | 71 | int blake2b_init( blake2b_state *S, const uint8_t outlen ); 72 | int blake2b_init_key( blake2b_state *S, const uint8_t outlen, const void *key, const uint8_t keylen ); 73 | int blake2b_init_param( blake2b_state *S, const blake2b_param *P ); 74 | int blake2b_update( blake2b_state *S, const uint8_t *in, uint64_t inlen ); 75 | int blake2b_final( blake2b_state *S, uint8_t *out, uint8_t outlen ); 76 | 77 | int blake2b( uint8_t *out, const void *in, const void *key, const uint8_t outlen, const uint64_t inlen, uint8_t keylen ); 78 | 79 | static inline int blake2( uint8_t *out, const void *in, const void *key, const uint8_t outlen, const uint64_t inlen, uint8_t keylen ) 80 | { 81 | return blake2b( out, in, key, outlen, inlen, keylen ); 82 | } 83 | 84 | #if defined(__cplusplus) 85 | } 86 | #endif 87 | 88 | #endif 89 | 90 | -------------------------------------------------------------------------------- /libasignify/blake2b-ref.c: -------------------------------------------------------------------------------- 1 | /* 2 | BLAKE2 reference source code package - reference C implementations 3 | 4 | Written in 2012 by Samuel Neves 5 | 6 | To the extent possible under law, the author(s) have dedicated all copyright 7 | and related and neighboring rights to this software to the public domain 8 | worldwide. This software is distributed without any warranty. 9 | 10 | You should have received a copy of the CC0 Public Domain Dedication along with 11 | this software. If not, see . 12 | */ 13 | 14 | #include 15 | #include 16 | #include 17 | 18 | #include "blake2.h" 19 | #include "blake2-impl.h" 20 | 21 | static const uint64_t blake2b_IV[8] = 22 | { 23 | 0x6a09e667f3bcc908ULL, 0xbb67ae8584caa73bULL, 24 | 0x3c6ef372fe94f82bULL, 0xa54ff53a5f1d36f1ULL, 25 | 0x510e527fade682d1ULL, 0x9b05688c2b3e6c1fULL, 26 | 0x1f83d9abfb41bd6bULL, 0x5be0cd19137e2179ULL 27 | }; 28 | 29 | static const uint8_t blake2b_sigma[12][16] = 30 | { 31 | { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } , 32 | { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 } , 33 | { 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 } , 34 | { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 } , 35 | { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 } , 36 | { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 } , 37 | { 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 } , 38 | { 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 } , 39 | { 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 } , 40 | { 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13 , 0 } , 41 | { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } , 42 | { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 } 43 | }; 44 | 45 | 46 | static inline int blake2b_set_lastnode( blake2b_state *S ) 47 | { 48 | S->f[1] = ~0ULL; 49 | return 0; 50 | } 51 | 52 | static inline int blake2b_clear_lastnode( blake2b_state *S ) 53 | { 54 | S->f[1] = 0ULL; 55 | return 0; 56 | } 57 | 58 | /* Some helper functions, not necessarily useful */ 59 | static inline int blake2b_set_lastblock( blake2b_state *S ) 60 | { 61 | if( S->last_node ) blake2b_set_lastnode( S ); 62 | 63 | S->f[0] = ~0ULL; 64 | return 0; 65 | } 66 | 67 | static inline int blake2b_clear_lastblock( blake2b_state *S ) 68 | { 69 | if( S->last_node ) blake2b_clear_lastnode( S ); 70 | 71 | S->f[0] = 0ULL; 72 | return 0; 73 | } 74 | 75 | static inline int blake2b_increment_counter( blake2b_state *S, const uint64_t inc ) 76 | { 77 | S->t[0] += inc; 78 | S->t[1] += ( S->t[0] < inc ); 79 | return 0; 80 | } 81 | 82 | 83 | 84 | // Parameter-related functions 85 | static inline int blake2b_param_set_digest_length( blake2b_param *P, const uint8_t digest_length ) 86 | { 87 | P->digest_length = digest_length; 88 | return 0; 89 | } 90 | 91 | static inline int blake2b_param_set_fanout( blake2b_param *P, const uint8_t fanout ) 92 | { 93 | P->fanout = fanout; 94 | return 0; 95 | } 96 | 97 | static inline int blake2b_param_set_max_depth( blake2b_param *P, const uint8_t depth ) 98 | { 99 | P->depth = depth; 100 | return 0; 101 | } 102 | 103 | static inline int blake2b_param_set_leaf_length( blake2b_param *P, const uint32_t leaf_length ) 104 | { 105 | store32( &P->leaf_length, leaf_length ); 106 | return 0; 107 | } 108 | 109 | static inline int blake2b_param_set_node_offset( blake2b_param *P, const uint64_t node_offset ) 110 | { 111 | store64( &P->node_offset, node_offset ); 112 | return 0; 113 | } 114 | 115 | static inline int blake2b_param_set_node_depth( blake2b_param *P, const uint8_t node_depth ) 116 | { 117 | P->node_depth = node_depth; 118 | return 0; 119 | } 120 | 121 | static inline int blake2b_param_set_inner_length( blake2b_param *P, const uint8_t inner_length ) 122 | { 123 | P->inner_length = inner_length; 124 | return 0; 125 | } 126 | 127 | static inline int blake2b_param_set_salt( blake2b_param *P, const uint8_t salt[BLAKE2B_SALTBYTES] ) 128 | { 129 | memcpy( P->salt, salt, BLAKE2B_SALTBYTES ); 130 | return 0; 131 | } 132 | 133 | static inline int blake2b_param_set_personal( blake2b_param *P, const uint8_t personal[BLAKE2B_PERSONALBYTES] ) 134 | { 135 | memcpy( P->personal, personal, BLAKE2B_PERSONALBYTES ); 136 | return 0; 137 | } 138 | 139 | static inline int blake2b_init0( blake2b_state *S ) 140 | { 141 | int i; 142 | 143 | memset( S, 0, sizeof( blake2b_state ) ); 144 | 145 | for( i = 0; i < 8; ++i ) S->h[i] = blake2b_IV[i]; 146 | 147 | return 0; 148 | } 149 | 150 | /* init xors IV with input parameter block */ 151 | int blake2b_init_param( blake2b_state *S, const blake2b_param *P ) 152 | { 153 | size_t i; 154 | blake2b_init0( S ); 155 | const uint8_t *p = ( const uint8_t * )( P ); 156 | 157 | /* IV XOR ParamBlock */ 158 | for( i = 0; i < 8; ++i ) 159 | S->h[i] ^= load64( p + sizeof( S->h[i] ) * i ); 160 | 161 | return 0; 162 | } 163 | 164 | 165 | 166 | int blake2b_init( blake2b_state *S, const uint8_t outlen ) 167 | { 168 | blake2b_param P[1]; 169 | 170 | if ( ( !outlen ) || ( outlen > BLAKE2B_OUTBYTES ) ) return -1; 171 | 172 | P->digest_length = outlen; 173 | P->key_length = 0; 174 | P->fanout = 1; 175 | P->depth = 1; 176 | store32( &P->leaf_length, 0 ); 177 | store64( &P->node_offset, 0 ); 178 | P->node_depth = 0; 179 | P->inner_length = 0; 180 | memset( P->reserved, 0, sizeof( P->reserved ) ); 181 | memset( P->salt, 0, sizeof( P->salt ) ); 182 | memset( P->personal, 0, sizeof( P->personal ) ); 183 | return blake2b_init_param( S, P ); 184 | } 185 | 186 | 187 | int blake2b_init_key( blake2b_state *S, const uint8_t outlen, const void *key, const uint8_t keylen ) 188 | { 189 | blake2b_param P[1]; 190 | 191 | if ( ( !outlen ) || ( outlen > BLAKE2B_OUTBYTES ) ) return -1; 192 | 193 | if ( !key || !keylen || keylen > BLAKE2B_KEYBYTES ) return -1; 194 | 195 | P->digest_length = outlen; 196 | P->key_length = keylen; 197 | P->fanout = 1; 198 | P->depth = 1; 199 | store32( &P->leaf_length, 0 ); 200 | store64( &P->node_offset, 0 ); 201 | P->node_depth = 0; 202 | P->inner_length = 0; 203 | memset( P->reserved, 0, sizeof( P->reserved ) ); 204 | memset( P->salt, 0, sizeof( P->salt ) ); 205 | memset( P->personal, 0, sizeof( P->personal ) ); 206 | 207 | if( blake2b_init_param( S, P ) < 0 ) return -1; 208 | 209 | { 210 | uint8_t block[BLAKE2B_BLOCKBYTES]; 211 | memset( block, 0, BLAKE2B_BLOCKBYTES ); 212 | memcpy( block, key, keylen ); 213 | blake2b_update( S, block, BLAKE2B_BLOCKBYTES ); 214 | secure_zero_memory( block, BLAKE2B_BLOCKBYTES ); /* Burn the key from stack */ 215 | } 216 | return 0; 217 | } 218 | 219 | static int blake2b_compress( blake2b_state *S, const uint8_t block[BLAKE2B_BLOCKBYTES] ) 220 | { 221 | uint64_t m[16]; 222 | uint64_t v[16]; 223 | int i; 224 | 225 | for( i = 0; i < 16; ++i ) 226 | m[i] = load64( block + i * sizeof( m[i] ) ); 227 | 228 | for( i = 0; i < 8; ++i ) 229 | v[i] = S->h[i]; 230 | 231 | v[ 8] = blake2b_IV[0]; 232 | v[ 9] = blake2b_IV[1]; 233 | v[10] = blake2b_IV[2]; 234 | v[11] = blake2b_IV[3]; 235 | v[12] = S->t[0] ^ blake2b_IV[4]; 236 | v[13] = S->t[1] ^ blake2b_IV[5]; 237 | v[14] = S->f[0] ^ blake2b_IV[6]; 238 | v[15] = S->f[1] ^ blake2b_IV[7]; 239 | #define G(r,i,a,b,c,d) \ 240 | do { \ 241 | a = a + b + m[blake2b_sigma[r][2*i+0]]; \ 242 | d = rotr64(d ^ a, 32); \ 243 | c = c + d; \ 244 | b = rotr64(b ^ c, 24); \ 245 | a = a + b + m[blake2b_sigma[r][2*i+1]]; \ 246 | d = rotr64(d ^ a, 16); \ 247 | c = c + d; \ 248 | b = rotr64(b ^ c, 63); \ 249 | } while(0) 250 | #define ROUND(r) \ 251 | do { \ 252 | G(r,0,v[ 0],v[ 4],v[ 8],v[12]); \ 253 | G(r,1,v[ 1],v[ 5],v[ 9],v[13]); \ 254 | G(r,2,v[ 2],v[ 6],v[10],v[14]); \ 255 | G(r,3,v[ 3],v[ 7],v[11],v[15]); \ 256 | G(r,4,v[ 0],v[ 5],v[10],v[15]); \ 257 | G(r,5,v[ 1],v[ 6],v[11],v[12]); \ 258 | G(r,6,v[ 2],v[ 7],v[ 8],v[13]); \ 259 | G(r,7,v[ 3],v[ 4],v[ 9],v[14]); \ 260 | } while(0) 261 | ROUND( 0 ); 262 | ROUND( 1 ); 263 | ROUND( 2 ); 264 | ROUND( 3 ); 265 | ROUND( 4 ); 266 | ROUND( 5 ); 267 | ROUND( 6 ); 268 | ROUND( 7 ); 269 | ROUND( 8 ); 270 | ROUND( 9 ); 271 | ROUND( 10 ); 272 | ROUND( 11 ); 273 | 274 | for( i = 0; i < 8; ++i ) 275 | S->h[i] = S->h[i] ^ v[i] ^ v[i + 8]; 276 | 277 | #undef G 278 | #undef ROUND 279 | return 0; 280 | } 281 | 282 | /* inlen now in bytes */ 283 | int blake2b_update( blake2b_state *S, const uint8_t *in, uint64_t inlen ) 284 | { 285 | while( inlen > 0 ) 286 | { 287 | size_t left = S->buflen; 288 | size_t fill = 2 * BLAKE2B_BLOCKBYTES - left; 289 | 290 | if( inlen > fill ) 291 | { 292 | memcpy( S->buf + left, in, fill ); // Fill buffer 293 | S->buflen += fill; 294 | blake2b_increment_counter( S, BLAKE2B_BLOCKBYTES ); 295 | blake2b_compress( S, S->buf ); // Compress 296 | memcpy( S->buf, S->buf + BLAKE2B_BLOCKBYTES, BLAKE2B_BLOCKBYTES ); // Shift buffer left 297 | S->buflen -= BLAKE2B_BLOCKBYTES; 298 | in += fill; 299 | inlen -= fill; 300 | } 301 | else // inlen <= fill 302 | { 303 | memcpy( S->buf + left, in, inlen ); 304 | S->buflen += inlen; // Be lazy, do not compress 305 | in += inlen; 306 | inlen -= inlen; 307 | } 308 | } 309 | 310 | return 0; 311 | } 312 | 313 | /* Is this correct? */ 314 | int blake2b_final( blake2b_state *S, uint8_t *out, uint8_t outlen ) 315 | { 316 | uint8_t buffer[BLAKE2B_OUTBYTES] = {0}; 317 | int i; 318 | 319 | if( outlen > BLAKE2B_OUTBYTES ) 320 | return -1; 321 | 322 | if( S->buflen > BLAKE2B_BLOCKBYTES ) 323 | { 324 | blake2b_increment_counter( S, BLAKE2B_BLOCKBYTES ); 325 | blake2b_compress( S, S->buf ); 326 | S->buflen -= BLAKE2B_BLOCKBYTES; 327 | memcpy( S->buf, S->buf + BLAKE2B_BLOCKBYTES, S->buflen ); 328 | } 329 | 330 | blake2b_increment_counter( S, S->buflen ); 331 | blake2b_set_lastblock( S ); 332 | memset( S->buf + S->buflen, 0, 2 * BLAKE2B_BLOCKBYTES - S->buflen ); /* Padding */ 333 | blake2b_compress( S, S->buf ); 334 | 335 | for( i = 0; i < 8; ++i ) /* Output full hash to temp buffer */ 336 | store64( buffer + sizeof( S->h[i] ) * i, S->h[i] ); 337 | 338 | memcpy( out, buffer, outlen ); 339 | return 0; 340 | } 341 | 342 | /* inlen, at least, should be uint64_t. Others can be size_t. */ 343 | int blake2b( uint8_t *out, const void *in, const void *key, const uint8_t outlen, const uint64_t inlen, uint8_t keylen ) 344 | { 345 | blake2b_state S[1]; 346 | 347 | /* Verify parameters */ 348 | if ( NULL == in ) return -1; 349 | 350 | if ( NULL == out ) return -1; 351 | 352 | if( NULL == key ) keylen = 0; 353 | 354 | if( keylen > 0 ) 355 | { 356 | if( blake2b_init_key( S, outlen, key, keylen ) < 0 ) return -1; 357 | } 358 | else 359 | { 360 | if( blake2b_init( S, outlen ) < 0 ) return -1; 361 | } 362 | 363 | blake2b_update( S, ( const uint8_t * )in, inlen ); 364 | blake2b_final( S, out, outlen ); 365 | return 0; 366 | } 367 | 368 | #if defined(BLAKE2B_SELFTEST) 369 | #include 370 | #include "blake2-kat.h" 371 | int main( int argc, char **argv ) 372 | { 373 | uint8_t key[BLAKE2B_KEYBYTES]; 374 | uint8_t buf[KAT_LENGTH]; 375 | 376 | for( size_t i = 0; i < BLAKE2B_KEYBYTES; ++i ) 377 | key[i] = ( uint8_t )i; 378 | 379 | for( size_t i = 0; i < KAT_LENGTH; ++i ) 380 | buf[i] = ( uint8_t )i; 381 | 382 | for( size_t i = 0; i < KAT_LENGTH; ++i ) 383 | { 384 | uint8_t hash[BLAKE2B_OUTBYTES]; 385 | blake2b( hash, buf, key, BLAKE2B_OUTBYTES, i, BLAKE2B_KEYBYTES ); 386 | 387 | if( 0 != memcmp( hash, blake2b_keyed_kat[i], BLAKE2B_OUTBYTES ) ) 388 | { 389 | puts( "error" ); 390 | return -1; 391 | } 392 | } 393 | 394 | puts( "ok" ); 395 | return 0; 396 | } 397 | #endif 398 | 399 | -------------------------------------------------------------------------------- /libasignify/chacha.c: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Public domain by Andrew Moon: https://github.com/floodyberry/chacha-opt 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include "chacha.h" 10 | #include "asignify_internal.h" 11 | 12 | enum chacha_constants { 13 | CHACHA_BLOCKBYTES = 64, 14 | }; 15 | 16 | CHACHA_ALIGN( 64 ) typedef struct chacha_state_internal_t { 17 | unsigned char s[48]; 18 | size_t rounds; 19 | size_t leftover; 20 | unsigned char buffer[CHACHA_BLOCKBYTES]; 21 | } chacha_state_internal; 22 | 23 | typedef uint32_t chacha_int32; 24 | 25 | /* interpret four 8 bit unsigned integers as a 32 bit unsigned integer in little endian */ 26 | static chacha_int32 27 | U8TO32(const unsigned char *p) { 28 | return 29 | (((chacha_int32)(p[0]) ) | 30 | (((chacha_int32)p[1]) << 8) | 31 | (((chacha_int32)p[2]) << 16) | 32 | (((chacha_int32)p[3]) << 24)); 33 | } 34 | 35 | /* store a 32 bit unsigned integer as four 8 bit unsigned integers in little endian */ 36 | static void 37 | U32TO8(unsigned char *p, chacha_int32 v) { 38 | p[0] = (v ) & 0xff; 39 | p[1] = (v >> 8) & 0xff; 40 | p[2] = (v >> 16) & 0xff; 41 | p[3] = (v >> 24) & 0xff; 42 | } 43 | 44 | /* 32 bit left rotate */ 45 | static chacha_int32 46 | ROTL32(chacha_int32 x, int k) { 47 | return ((x << k) | (x >> (32 - k))) & 0xffffffff; 48 | } 49 | 50 | /* "expand 32-byte k", as 4 little endian 32-bit unsigned integers */ 51 | static const chacha_int32 chacha_constants[4] = { 52 | 0x61707865, 0x3320646e, 0x79622d32, 0x6b206574 53 | }; 54 | 55 | static void 56 | chacha_blocks(chacha_state_internal *state, const unsigned char *in, 57 | unsigned char *out, size_t bytes) 58 | { 59 | CHACHA_ALIGN( 64 ) chacha_int32 x[16], j[12]; 60 | chacha_int32 t; 61 | unsigned char *ctarget = out, tmp[64]; 62 | size_t i, r; 63 | 64 | if (!bytes) return; 65 | 66 | j[0] = U8TO32(state->s + 0); 67 | j[1] = U8TO32(state->s + 4); 68 | j[2] = U8TO32(state->s + 8); 69 | j[3] = U8TO32(state->s + 12); 70 | j[4] = U8TO32(state->s + 16); 71 | j[5] = U8TO32(state->s + 20); 72 | j[6] = U8TO32(state->s + 24); 73 | j[7] = U8TO32(state->s + 28); 74 | j[8] = U8TO32(state->s + 32); 75 | j[9] = U8TO32(state->s + 36); 76 | j[10] = U8TO32(state->s + 40); 77 | j[11] = U8TO32(state->s + 44); 78 | 79 | r = state->rounds; 80 | 81 | for (;;) { 82 | if (bytes < 64) { 83 | if (in) { 84 | for (i = 0; i < bytes; i++) tmp[i] = in[i]; 85 | in = tmp; 86 | } 87 | ctarget = out; 88 | out = tmp; 89 | } 90 | 91 | x[0] = chacha_constants[0]; 92 | x[1] = chacha_constants[1]; 93 | x[2] = chacha_constants[2]; 94 | x[3] = chacha_constants[3]; 95 | x[4] = j[0]; 96 | x[5] = j[1]; 97 | x[6] = j[2]; 98 | x[7] = j[3]; 99 | x[8] = j[4]; 100 | x[9] = j[5]; 101 | x[10] = j[6]; 102 | x[11] = j[7]; 103 | x[12] = j[8]; 104 | x[13] = j[9]; 105 | x[14] = j[10]; 106 | x[15] = j[11]; 107 | 108 | #define quarter(a,b,c,d) \ 109 | a += b; t = d^a; d = ROTL32(t,16); \ 110 | c += d; t = b^c; b = ROTL32(t,12); \ 111 | a += b; t = d^a; d = ROTL32(t, 8); \ 112 | c += d; t = b^c; b = ROTL32(t, 7); 113 | 114 | #define doubleround() \ 115 | quarter( x[0], x[4], x[8],x[12]) \ 116 | quarter( x[1], x[5], x[9],x[13]) \ 117 | quarter( x[2], x[6],x[10],x[14]) \ 118 | quarter( x[3], x[7],x[11],x[15]) \ 119 | quarter( x[0], x[5],x[10],x[15]) \ 120 | quarter( x[1], x[6],x[11],x[12]) \ 121 | quarter( x[2], x[7], x[8],x[13]) \ 122 | quarter( x[3], x[4], x[9],x[14]) 123 | 124 | i = r; 125 | do { 126 | doubleround() 127 | i -= 2; 128 | } while (i); 129 | 130 | x[0] += chacha_constants[0]; 131 | x[1] += chacha_constants[1]; 132 | x[2] += chacha_constants[2]; 133 | x[3] += chacha_constants[3]; 134 | x[4] += j[0]; 135 | x[5] += j[1]; 136 | x[6] += j[2]; 137 | x[7] += j[3]; 138 | x[8] += j[4]; 139 | x[9] += j[5]; 140 | x[10] += j[6]; 141 | x[11] += j[7]; 142 | x[12] += j[8]; 143 | x[13] += j[9]; 144 | x[14] += j[10]; 145 | x[15] += j[11]; 146 | 147 | if (in) { 148 | U32TO8(out + 0, x[0] ^ U8TO32(in + 0)); 149 | U32TO8(out + 4, x[1] ^ U8TO32(in + 4)); 150 | U32TO8(out + 8, x[2] ^ U8TO32(in + 8)); 151 | U32TO8(out + 12, x[3] ^ U8TO32(in + 12)); 152 | U32TO8(out + 16, x[4] ^ U8TO32(in + 16)); 153 | U32TO8(out + 20, x[5] ^ U8TO32(in + 20)); 154 | U32TO8(out + 24, x[6] ^ U8TO32(in + 24)); 155 | U32TO8(out + 28, x[7] ^ U8TO32(in + 28)); 156 | U32TO8(out + 32, x[8] ^ U8TO32(in + 32)); 157 | U32TO8(out + 36, x[9] ^ U8TO32(in + 36)); 158 | U32TO8(out + 40, x[10] ^ U8TO32(in + 40)); 159 | U32TO8(out + 44, x[11] ^ U8TO32(in + 44)); 160 | U32TO8(out + 48, x[12] ^ U8TO32(in + 48)); 161 | U32TO8(out + 52, x[13] ^ U8TO32(in + 52)); 162 | U32TO8(out + 56, x[14] ^ U8TO32(in + 56)); 163 | U32TO8(out + 60, x[15] ^ U8TO32(in + 60)); 164 | in += 64; 165 | } else { 166 | U32TO8(out + 0, x[0]); 167 | U32TO8(out + 4, x[1]); 168 | U32TO8(out + 8, x[2]); 169 | U32TO8(out + 12, x[3]); 170 | U32TO8(out + 16, x[4]); 171 | U32TO8(out + 20, x[5]); 172 | U32TO8(out + 24, x[6]); 173 | U32TO8(out + 28, x[7]); 174 | U32TO8(out + 32, x[8]); 175 | U32TO8(out + 36, x[9]); 176 | U32TO8(out + 40, x[10]); 177 | U32TO8(out + 44, x[11]); 178 | U32TO8(out + 48, x[12]); 179 | U32TO8(out + 52, x[13]); 180 | U32TO8(out + 56, x[14]); 181 | U32TO8(out + 60, x[15]); 182 | } 183 | 184 | /* increment the 64 bit counter, split in to two 32 bit halves */ 185 | j[8]++; 186 | if (!j[8]) 187 | j[9]++; 188 | 189 | if (bytes <= 64) { 190 | if (bytes < 64) for (i = 0; i < bytes; i++) ctarget[i] = out[i]; 191 | 192 | /* store the counter back to the state */ 193 | U32TO8(state->s + 32, j[8]); 194 | U32TO8(state->s + 36, j[9]); 195 | goto cleanup; 196 | } 197 | bytes -= 64; 198 | out += 64; 199 | } 200 | 201 | cleanup: 202 | explicit_memzero(j, sizeof(j)); 203 | } 204 | 205 | /* is the pointer aligned on a word boundary? */ 206 | static int 207 | chacha_is_aligned(const void *p) { 208 | return ((size_t)p & (sizeof(size_t) - 1)) == 0; 209 | } 210 | 211 | /* initialize the state */ 212 | void 213 | chacha_init(chacha_state *S, const chacha_key *key, const chacha_iv *iv, size_t rounds) 214 | { 215 | chacha_state_internal *state = (chacha_state_internal *)S; 216 | memcpy(state->s + 0, key, 32); 217 | memset(state->s + 32, 0, 8); 218 | memcpy(state->s + 40, iv, 8); 219 | state->rounds = rounds; 220 | state->leftover = 0; 221 | } 222 | 223 | /* processes inlen bytes (can do partial blocks), handling input/ouput alignment */ 224 | static void 225 | chacha_consume(chacha_state_internal *state, const unsigned char *in, unsigned char *out, size_t inlen) 226 | { 227 | unsigned char buffer[16 * CHACHA_BLOCKBYTES]; 228 | int in_aligned, out_aligned; 229 | 230 | /* it's ok to call with 0 bytes */ 231 | if (!inlen) 232 | return; 233 | 234 | /* if everything is aligned, handle directly */ 235 | in_aligned = chacha_is_aligned(in); 236 | out_aligned = chacha_is_aligned(out); 237 | if (in_aligned && out_aligned) { 238 | chacha_blocks(state, in, out, inlen); 239 | return; 240 | } 241 | 242 | /* copy the unaligned data to an aligned buffer and process in chunks */ 243 | while (inlen) { 244 | const size_t bytes = (inlen > sizeof(buffer)) ? sizeof(buffer) : inlen; 245 | const unsigned char *src = in; 246 | unsigned char *dst = (out_aligned) ? out : buffer; 247 | if (!in_aligned) { 248 | memcpy(buffer, in, bytes); 249 | src = buffer; 250 | } 251 | chacha_blocks(state, src, dst, bytes); 252 | if (!out_aligned) 253 | memcpy(out, buffer, bytes); 254 | if (in) in += bytes; 255 | out += bytes; 256 | inlen -= bytes; 257 | } 258 | } 259 | 260 | 261 | /* update, returns number of bytes written to out */ 262 | size_t 263 | chacha_update(chacha_state *S, const unsigned char *in, unsigned char *out, size_t inlen) 264 | { 265 | chacha_state_internal *state = (chacha_state_internal *)S; 266 | unsigned char *out_start = out; 267 | size_t bytes; 268 | 269 | /* enough for at least one block? */ 270 | if ((state->leftover + inlen) >= CHACHA_BLOCKBYTES) { 271 | /* handle the previous data */ 272 | if (state->leftover) { 273 | bytes = (CHACHA_BLOCKBYTES - state->leftover); 274 | if (in) { 275 | memcpy(state->buffer + state->leftover, in, bytes); 276 | in += bytes; 277 | } 278 | chacha_consume(state, (in) ? state->buffer : NULL, out, CHACHA_BLOCKBYTES); 279 | inlen -= bytes; 280 | out += CHACHA_BLOCKBYTES; 281 | state->leftover = 0; 282 | } 283 | 284 | /* handle the direct data */ 285 | bytes = (inlen & ~(CHACHA_BLOCKBYTES - 1)); 286 | if (bytes) { 287 | chacha_consume(state, in, out, bytes); 288 | inlen -= bytes; 289 | if (in) in += bytes; 290 | out += bytes; 291 | } 292 | } 293 | 294 | /* handle leftover data */ 295 | if (inlen) { 296 | if (in) memcpy(state->buffer + state->leftover, in, inlen); 297 | else memset(state->buffer + state->leftover, 0, inlen); 298 | state->leftover += inlen; 299 | } 300 | 301 | return out - out_start; 302 | } 303 | 304 | /* finalize, write out any leftover data */ 305 | size_t 306 | chacha_final(chacha_state *S, unsigned char *out) { 307 | chacha_state_internal *state = (chacha_state_internal *)S; 308 | size_t ret; 309 | 310 | if (state->leftover) { 311 | if (chacha_is_aligned(out)) { 312 | chacha_blocks(state, state->buffer, out, state->leftover); 313 | } else { 314 | chacha_blocks(state, state->buffer, state->buffer, state->leftover); 315 | memcpy(out, state->buffer, state->leftover); 316 | } 317 | } 318 | 319 | ret = state->leftover; 320 | explicit_memzero(S, sizeof(chacha_state)); 321 | 322 | return ret; 323 | } 324 | -------------------------------------------------------------------------------- /libasignify/chacha.h: -------------------------------------------------------------------------------- 1 | #ifndef CHACHA_H 2 | #define CHACHA_H 3 | 4 | #include 5 | 6 | #ifndef CHACHA_ALIGN 7 | # if defined(_MSC_VER) 8 | # define CHACHA_ALIGN(x) __declspec(align(x)) 9 | # else 10 | # define CHACHA_ALIGN(x) __attribute__((aligned(x))) 11 | # endif 12 | #endif 13 | 14 | #if defined(__cplusplus) 15 | extern "C" { 16 | #endif 17 | 18 | CHACHA_ALIGN( 64 ) typedef struct chacha_state_t { 19 | unsigned char opaque[128]; 20 | } chacha_state; 21 | 22 | typedef struct chacha_key_t { 23 | unsigned char b[32]; 24 | } chacha_key; 25 | 26 | typedef struct chacha_iv_t { 27 | unsigned char b[8]; 28 | } chacha_iv; 29 | 30 | typedef struct chacha_iv24_t { 31 | unsigned char b[24]; 32 | } chacha_iv24; 33 | 34 | void chacha_init(chacha_state *S, const chacha_key *key, const chacha_iv *iv, size_t rounds); 35 | size_t chacha_update(chacha_state *S, const unsigned char *in, unsigned char *out, size_t inlen); 36 | size_t chacha_final(chacha_state *S, unsigned char *out); 37 | 38 | #if defined(__cplusplus) 39 | } 40 | #endif 41 | 42 | #endif /* CHACHA_H */ 43 | 44 | -------------------------------------------------------------------------------- /libasignify/generate.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015, Vsevolod Stakhov 3 | * 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * * Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY AUTHOR ''AS IS'' AND ANY 15 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY 18 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | */ 25 | 26 | #ifdef HAVE_CONFIG_H 27 | #include "config.h" 28 | #endif 29 | 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | #ifdef HAVE_MLOCK 36 | #include 37 | #endif 38 | 39 | #include "asignify.h" 40 | #include "asignify_internal.h" 41 | #include "blake2.h" 42 | #include "tweetnacl.h" 43 | 44 | static bool 45 | asignify_encrypt_privkey(struct asignify_private_key *privk, unsigned int rounds, 46 | asignify_password_cb password_cb, void *d) 47 | { 48 | unsigned char canary[10]; 49 | unsigned char xorkey[crypto_sign_SECRETKEYBYTES]; 50 | char password[1024]; 51 | int r; 52 | bool ret = false; 53 | 54 | privk->checksum = xmalloc(BLAKE2B_OUTBYTES); 55 | privk->salt = xmalloc(SALT_LEN); 56 | privk->rounds = rounds; 57 | privk->pbkdf_alg = PBKDF_ALG; 58 | randombytes(privk->salt, SALT_LEN); 59 | blake2b(privk->checksum, privk->encrypted_blob, NULL, BLAKE2B_OUTBYTES, 60 | crypto_sign_SECRETKEYBYTES, 0); 61 | 62 | randombytes(canary, sizeof(canary)); 63 | memcpy(password + sizeof(password) - sizeof(canary), canary, 64 | sizeof(canary)); 65 | r = password_cb(password, sizeof(password) - sizeof(canary), d); 66 | if (r <= 0 || r > sizeof(password) - sizeof(canary) || 67 | memcmp(password + sizeof(password) - sizeof(canary), canary, sizeof(canary)) != 0) { 68 | goto cleanup; 69 | } 70 | 71 | if (pkcs5_pbkdf2(password, r, privk->salt, SALT_LEN, xorkey, sizeof(xorkey), 72 | privk->rounds) == -1) { 73 | goto cleanup; 74 | } 75 | 76 | explicit_memzero(password, sizeof(password)); 77 | 78 | for (r = 0; r < sizeof(xorkey); r ++) { 79 | privk->encrypted_blob[r] ^= xorkey[r]; 80 | } 81 | 82 | explicit_memzero(xorkey, sizeof(xorkey)); 83 | ret = true; 84 | cleanup: 85 | if (!ret) { 86 | free(privk->salt); 87 | privk->salt = NULL; 88 | free(privk->checksum); 89 | privk->checksum = NULL; 90 | } 91 | return (ret); 92 | } 93 | 94 | static bool 95 | asignify_generate_v1(FILE *privf, FILE *pubf, unsigned int rounds, 96 | asignify_password_cb password_cb, void *d) 97 | { 98 | 99 | struct asignify_private_key *privk; 100 | struct asignify_public_data *pubk; 101 | bool ret = false; 102 | 103 | if (privf == NULL || pubf == NULL) { 104 | return (false); 105 | } 106 | if (rounds != 0 && password_cb == NULL) { 107 | return (false); 108 | } 109 | if (rounds != 0 && rounds < PBKDF_MINROUNDS) { 110 | return (false); 111 | } 112 | 113 | privk = xmalloc0(sizeof(*privk)); 114 | pubk = xmalloc0(sizeof(*pubk)); 115 | 116 | privk->version = 1; 117 | privk->id = xmalloc(KEY_ID_LEN); 118 | randombytes(privk->id, KEY_ID_LEN); 119 | 120 | pubk->version = 1; 121 | pubk->data_len = crypto_sign_PUBLICKEYBYTES; 122 | pubk->id_len = KEY_ID_LEN; 123 | asignify_alloc_public_data_fields(pubk); 124 | 125 | memcpy(pubk->id, privk->id, KEY_ID_LEN); 126 | 127 | privk->encrypted_blob = xmalloc(crypto_sign_SECRETKEYBYTES); 128 | crypto_sign_keypair(pubk->data, privk->encrypted_blob); 129 | 130 | if (rounds > 0) { 131 | if (!asignify_encrypt_privkey(privk, rounds, password_cb, d)) { 132 | goto cleanup; 133 | } 134 | } 135 | 136 | ret = asignify_pubkey_write(pubk, pubf); 137 | if (ret) { 138 | ret = asignify_privkey_write(privk, privf); 139 | } 140 | 141 | cleanup: 142 | asignify_public_data_free(pubk); 143 | 144 | explicit_memzero(privk->encrypted_blob, crypto_sign_SECRETKEYBYTES); 145 | free(privk->encrypted_blob); 146 | free(privk->id); 147 | free(privk); 148 | 149 | return (ret); 150 | } 151 | 152 | static bool 153 | asignify_generate_pubkey_internal(struct asignify_private_data *privd, 154 | FILE *pubf) 155 | { 156 | 157 | struct asignify_public_data *pubk; 158 | bool ret = true; 159 | 160 | if (privd == NULL || pubf == NULL) { 161 | return (false); 162 | } 163 | 164 | #define PUBKEY_OFF (crypto_sign_SECRETKEYBYTES - crypto_sign_PUBLICKEYBYTES) 165 | pubk = xmalloc0(sizeof(*pubk)); 166 | pubk->version = 1; 167 | pubk->id = xmalloc(KEY_ID_LEN); 168 | pubk->id_len = KEY_ID_LEN; 169 | memcpy(pubk->id, privd->id, KEY_ID_LEN); 170 | pubk->data_len = crypto_sign_PUBLICKEYBYTES; 171 | pubk->data = xmalloc(pubk->data_len); 172 | memcpy(pubk->data, privd->data + PUBKEY_OFF, pubk->data_len); 173 | 174 | ret = asignify_pubkey_write(pubk, pubf); 175 | 176 | asignify_public_data_free(pubk); 177 | fclose(pubf); 178 | 179 | return (ret); 180 | } 181 | 182 | #define HEX_OUT_PRIVK(privk, field, name, size, f) do { \ 183 | hexdata = xmalloc((size) * 2 + 1); \ 184 | if(bin2hex(hexdata, (size) * 2 + 1, privk->field, (size)) == NULL) { \ 185 | abort(); \ 186 | } \ 187 | fprintf(f, "%s: %s\n", (name), hexdata); \ 188 | free(hexdata); \ 189 | } while (0) 190 | 191 | bool 192 | asignify_privkey_write(struct asignify_private_key *privk, FILE *f) 193 | { 194 | char *hexdata; 195 | 196 | if (privk == NULL || f == NULL) { 197 | return (false); 198 | } 199 | 200 | if (privk->version != 1) { 201 | return (false); 202 | } 203 | 204 | fprintf(f, PRIVKEY_MAGIC "\n" "version: %u\n", privk->version); 205 | HEX_OUT_PRIVK(privk, encrypted_blob, "data", crypto_sign_SECRETKEYBYTES, f); 206 | 207 | if (privk->id) { 208 | HEX_OUT_PRIVK(privk, id, "id", KEY_ID_LEN, f); 209 | } 210 | 211 | /* Encrypted privkey */ 212 | if (privk->pbkdf_alg != NULL) { 213 | fprintf(f, "kdf: %s\n", privk->pbkdf_alg); 214 | fprintf(f, "rounds: %u\n", privk->rounds); 215 | HEX_OUT_PRIVK(privk, salt, "salt", SALT_LEN, f); 216 | HEX_OUT_PRIVK(privk, checksum, "checksum", BLAKE2B_OUTBYTES, f); 217 | } 218 | 219 | return (true); 220 | } 221 | 222 | bool 223 | asignify_generate(const char *privkf, const char *pubkf, unsigned int version, 224 | unsigned int rounds, asignify_password_cb password_cb, void *d) 225 | { 226 | FILE *privf, *pubf; 227 | bool ret = false; 228 | 229 | if (version != 1) 230 | return (false); 231 | 232 | privf = xfopen(privkf, "w"); 233 | pubf = xfopen(pubkf, "w"); 234 | 235 | if (!privf || !pubf) { 236 | goto cleanup; 237 | } 238 | 239 | ret = asignify_generate_v1(privf, pubf, rounds, password_cb, d); 240 | cleanup: 241 | if (pubf != NULL) 242 | fclose(pubf); 243 | if (privf != NULL) 244 | fclose(privf); 245 | return (ret); 246 | } 247 | 248 | bool 249 | asignify_generate_pubkey(const char *privkf, const char *pubkf, 250 | asignify_password_cb password_cb, void *d) 251 | { 252 | FILE *privf, *pubf; 253 | struct asignify_private_data *privd = NULL; 254 | int error; 255 | bool ret; 256 | 257 | privf = xfopen(privkf, "r"); 258 | pubf = xfopen(pubkf, "w"); 259 | 260 | if (!privf || !pubf) { 261 | return (false); 262 | } 263 | 264 | privd = asignify_private_data_load(privf, &error, password_cb, d); 265 | if (privd == NULL) { 266 | /* XXX */ 267 | (void)error; 268 | return (false); 269 | } 270 | 271 | ret = asignify_generate_pubkey_internal(privd, pubf); 272 | 273 | asignify_private_data_free(privd); 274 | return (ret); 275 | } 276 | 277 | bool 278 | asignify_privkey_from_ssh(const char *sshkf, const char *privkf, 279 | unsigned int version, unsigned int rounds, 280 | asignify_password_cb password_cb, void *d) 281 | { 282 | FILE *privf, *sshf; 283 | struct asignify_private_data *privd = NULL; 284 | struct asignify_private_key privk; 285 | bool ret = false; 286 | 287 | if (version != 1) 288 | return (false); 289 | 290 | privf = NULL; 291 | sshf = xfopen(sshkf, "r"); 292 | 293 | if (!sshf) { 294 | return (false); 295 | } 296 | 297 | privd = asignify_ssh_privkey_load(sshf, NULL); 298 | if (privd == NULL) { 299 | goto cleanup; 300 | } 301 | 302 | privf = xfopen(privkf, "w"); 303 | if (privf == NULL) { 304 | goto cleanup; 305 | } 306 | 307 | memset(&privk, 0, sizeof(privk)); 308 | privk.encrypted_blob = privd->data; 309 | privk.version = version; 310 | privk.id = NULL; 311 | 312 | if (password_cb != NULL) { 313 | if (!asignify_encrypt_privkey(&privk, rounds, password_cb, d)) { 314 | goto cleanup; 315 | } 316 | } 317 | 318 | ret = asignify_privkey_write(&privk, privf); 319 | 320 | cleanup: 321 | asignify_private_data_free(privd); 322 | if (sshf != NULL) 323 | fclose(sshf); 324 | if (privf != NULL) 325 | fclose(privf); 326 | 327 | return (ret); 328 | } 329 | -------------------------------------------------------------------------------- /libasignify/kvec.h: -------------------------------------------------------------------------------- 1 | /* The MIT License 2 | 3 | Copyright (c) 2008, by Attractive Chaos 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 20 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 21 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | */ 25 | 26 | /* 27 | An example: 28 | 29 | #include "kvec.h" 30 | int main() { 31 | kvec_t(int) array; 32 | kv_init(array); 33 | kv_push(int, array, 10); // append 34 | kv_a(int, array, 20) = 5; // dynamic 35 | kv_A(array, 20) = 4; // static 36 | kv_destroy(array); 37 | return 0; 38 | } 39 | */ 40 | 41 | /* 42 | 2008-09-22 (0.1.0): 43 | 44 | * The initial version. 45 | 46 | */ 47 | 48 | #ifndef AC_KVEC_H 49 | #define AC_KVEC_H 50 | 51 | #include 52 | 53 | #define kv_roundup32(x) (--(x), (x)|=(x)>>1, (x)|=(x)>>2, (x)|=(x)>>4, (x)|=(x)>>8, (x)|=(x)>>16, ++(x)) 54 | 55 | #define kvec_t(type) struct { size_t n, m; type *a; } 56 | #define kv_init(v) ((v).n = (v).m = 0, (v).a = 0) 57 | #define kv_destroy(v) free((v).a) 58 | #define kv_A(v, i) ((v).a[(i)]) 59 | #define kv_pop(v) ((v).a[--(v).n]) 60 | #define kv_size(v) ((v).n) 61 | #define kv_max(v) ((v).m) 62 | 63 | #define xrealloc(type, p,l) do { \ 64 | (p) = (type*)realloc((p), (l)); \ 65 | if ((p) == NULL) { \ 66 | abort(); \ 67 | } \ 68 | } while (0) 69 | 70 | #define kv_resize(type, v, s) do { \ 71 | (v).m = (s); \ 72 | xrealloc(type, (v).a, sizeof(type) * (v).m)); \ 73 | } while (0) 74 | 75 | #define kv_grow_factor 1.5 76 | #define kv_grow(type, v) do { \ 77 | (v).m = ((v).m > 1 ? (v).m * kv_grow_factor : 2); \ 78 | xrealloc(type, (v).a, sizeof(type) * (v).m); \ 79 | } while (0) 80 | 81 | #define kv_reserve(type, v, c) do { \ 82 | ((v).m = ((v).m + (c)) * kv_grow_factor); \ 83 | xrealloc(type, (v).a, sizeof(type) * (v).m); \ 84 | } while (0) 85 | 86 | #define kv_copy(type, v1, v0) do { \ 87 | if ((v1).m < (v0).n) kv_resize(type, v1, (v0).n); \ 88 | (v1).n = (v0).n; \ 89 | memcpy((v1).a, (v0).a, sizeof(type) * (v0).n); \ 90 | } while (0) \ 91 | 92 | #define kv_push(type, v, x) do { \ 93 | if ((v).n == (v).m) { \ 94 | kv_grow(type, v); \ 95 | } \ 96 | (v).a[(v).n++] = (x); \ 97 | } while (0) 98 | 99 | #define kv_push_a(type, v, x, c) do { \ 100 | while ((v).m <= ((v).n + (c))) { \ 101 | kv_reserve(type, v, c); \ 102 | } \ 103 | memcpy(&(v).a[(v).n], (x), (c) * sizeof(type)); \ 104 | (v).n += (c); \ 105 | } while (0) 106 | 107 | #define kv_prepend(type, v, x) do { \ 108 | if ((v).n == (v).m) { \ 109 | kv_grow(type, v); \ 110 | } \ 111 | memmove((v).a + 1, (v).a, sizeof(type) * (v).n); \ 112 | (v).a[0] = (x); \ 113 | (v).n ++; \ 114 | } while (0) 115 | 116 | #define kv_concat(type, v1, v0) do { \ 117 | if ((v1).m < (v0).n + (v1).n) kv_resize(type, v1, (v0).n + (v1).n); \ 118 | memcpy((v1).a + (v1).n, (v0).a, sizeof(type) * ((v0).n + (v1).n)); \ 119 | (v1).n = (v0).n + (v1).n; \ 120 | } while (0) 121 | 122 | #define kv_del(type, v, i) do { \ 123 | if ((i) < (v).n) { \ 124 | memmove((v).a + (i), (v).a + ((i) + 1), sizeof(type) * ((v).n - (i) - 1)); \ 125 | (v).n --; \ 126 | } \ 127 | } while (0) 128 | 129 | #endif 130 | -------------------------------------------------------------------------------- /libasignify/libasignify.ver: -------------------------------------------------------------------------------- 1 | LIBASIGNIFY_1.0 { 2 | global: 3 | asignify_verify_init; 4 | asignify_verify_load_pubkey; 5 | asignify_verify_load_signature; 6 | asignify_verify_file; 7 | asignify_verify_get_error; 8 | asignify_verify_free; 9 | 10 | asignify_sign_init; 11 | asignify_sign_load_privkey; 12 | asignify_sign_add_file; 13 | asignify_sign_write_signature; 14 | asignify_sign_get_error; 15 | asignify_sign_free; 16 | 17 | asignify_generate; 18 | asignify_generate_pubkey; 19 | 20 | explicit_memzero; 21 | 22 | asignify_digest_len; 23 | asignify_digest_name; 24 | asignify_digest_fd; 25 | asignify_digest_from_str; 26 | 27 | asignify_privkey_from_ssh; 28 | 29 | asignify_encrypt_init; 30 | asignify_encrypt_load_pubkey; 31 | asignify_encrypt_load_privkey; 32 | asignify_encrypt_crypt_file; 33 | asignify_encrypt_decrypt_file; 34 | asignify_encrypt_get_error; 35 | asignify_encrypt_free; 36 | 37 | # CSU symbols, sigh. 38 | __progname; 39 | environ; 40 | local: 41 | *; 42 | }; 43 | -------------------------------------------------------------------------------- /libasignify/pbkdf2.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015, Vsevolod Stakhov 3 | * 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * * Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY AUTHOR ''AS IS'' AND ANY 15 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY 18 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | */ 25 | 26 | #ifdef HAVE_CONFIG_H 27 | #include "config.h" 28 | #endif 29 | 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include "asignify_internal.h" 36 | #include "blake2.h" 37 | 38 | /* 39 | * Password-Based Key Derivation Function 2 (PKCS #5 v2.0). 40 | * Code based on IEEE Std 802.11-2007, Annex H.4.2. 41 | */ 42 | int 43 | pkcs5_pbkdf2(const char *pass, size_t pass_len, const uint8_t *salt, 44 | size_t salt_len, uint8_t *key, size_t key_len, unsigned int rounds) 45 | { 46 | uint8_t *asalt, obuf[BLAKE2B_OUTBYTES]; 47 | uint8_t d1[BLAKE2B_OUTBYTES], d2[BLAKE2B_OUTBYTES]; 48 | unsigned int i, j; 49 | unsigned int count; 50 | size_t r; 51 | 52 | if (rounds < 1 || key_len == 0) { 53 | return (-1); 54 | } 55 | if (salt_len == 0 || salt_len > SIZE_MAX - 4) { 56 | return (-1); 57 | } 58 | 59 | asalt = xmalloc(salt_len + 4); 60 | memcpy(asalt, salt, salt_len); 61 | 62 | for (count = 1; key_len > 0; count++) { 63 | asalt[salt_len + 0] = (count >> 24) & 0xff; 64 | asalt[salt_len + 1] = (count >> 16) & 0xff; 65 | asalt[salt_len + 2] = (count >> 8) & 0xff; 66 | asalt[salt_len + 3] = count & 0xff; 67 | blake2b(d1, asalt, pass, BLAKE2B_OUTBYTES, salt_len + 4, pass_len); 68 | memcpy(obuf, d1, sizeof(obuf)); 69 | 70 | for (i = 1; i < rounds; i++) { 71 | blake2b(d2, d1, pass, BLAKE2B_OUTBYTES, BLAKE2B_OUTBYTES, pass_len); 72 | memcpy(d1, d2, sizeof(d1)); 73 | for (j = 0; j < sizeof(obuf); j++) 74 | obuf[j] ^= d1[j]; 75 | } 76 | 77 | r = MIN(key_len, BLAKE2B_OUTBYTES); 78 | memcpy(key, obuf, r); 79 | key += r; 80 | key_len -= r; 81 | } 82 | 83 | explicit_memzero(asalt, salt_len + 4); 84 | free(asalt); 85 | explicit_memzero(d1, sizeof(d1)); 86 | explicit_memzero(d2, sizeof(d2)); 87 | explicit_memzero(obuf, sizeof(obuf)); 88 | 89 | return (0); 90 | } 91 | -------------------------------------------------------------------------------- /libasignify/pubkey.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2015, Vsevolod Stakhov 2 | * All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are met: 6 | * * Redistributions of source code must retain the above copyright 7 | * notice, this list of conditions and the following disclaimer. 8 | * * Redistributions in binary form must reproduce the above copyright 9 | * notice, this list of conditions and the following disclaimer in the 10 | * documentation and/or other materials provided with the distribution. 11 | * 12 | * THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY 13 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 14 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 15 | * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY 16 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 17 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 18 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 19 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 20 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 21 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 22 | */ 23 | 24 | #ifdef HAVE_CONFIG_H 25 | #include "config.h" 26 | #endif 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #include "asignify.h" 34 | #include "asignify_internal.h" 35 | #include "tweetnacl.h" 36 | #include "sha2.h" 37 | #include "blake2.h" 38 | 39 | #define PUBKEY_MAGIC "asignify-pubkey:" 40 | #define OBSD_PKALG "Ed" 41 | #define PUBKEY_VER_MAX 1 42 | #define PUBKEY_KEY_LEN crypto_sign_ed25519_PUBLICKEYBYTES 43 | 44 | #define SSH_KEY_MAGIC "ssh-ed25519" 45 | 46 | struct obsd_pubkey { 47 | uint8_t pkalg[2]; 48 | uint8_t keynum[8]; 49 | uint8_t pubkey[crypto_sign_ed25519_PUBLICKEYBYTES]; 50 | }; 51 | 52 | static bool 53 | asignify_pubkey_try_obsd(const char *buf, size_t buflen, 54 | struct asignify_public_data **pk) 55 | { 56 | struct obsd_pubkey opk; 57 | struct asignify_public_data *res; 58 | 59 | if (buflen >= sizeof(OBSD_COMMENTHDR) - 1 && 60 | memcmp(buf, OBSD_COMMENTHDR, sizeof(OBSD_COMMENTHDR) - 1) == 0) { 61 | /* Skip comments */ 62 | return (true); 63 | } 64 | 65 | if (b64_pton(buf, (unsigned char *)&opk, sizeof(opk)) != sizeof(opk)) { 66 | return (false); 67 | } 68 | if (memcmp(opk.pkalg, OBSD_PKALG, sizeof(opk.pkalg)) != 0) { 69 | return (false); 70 | } 71 | 72 | res = xmalloc0(sizeof(*res)); 73 | /* OpenBSD version code */ 74 | res->version = 0; 75 | res->data_len = sizeof(opk.pubkey); 76 | res->id_len = sizeof(opk.keynum); 77 | asignify_alloc_public_data_fields(res); 78 | memcpy(res->data, opk.pubkey, res->data_len); 79 | memcpy(res->id, opk.keynum, res->id_len); 80 | 81 | *pk = res; 82 | 83 | /* 84 | * We stop processing on the first non-comment line. If we have a key, 85 | * then we set *pk to some openbsd key, otherwise this variable is not 86 | * touched 87 | */ 88 | return (false); 89 | } 90 | 91 | static bool 92 | asignify_pubkey_try_ssh(const char *buf, size_t buflen, 93 | struct asignify_public_data **pk) 94 | { 95 | unsigned char pkbuf[crypto_sign_ed25519_PUBLICKEYBYTES * 2]; 96 | const unsigned char *tok, *bpos; 97 | const char *p = buf; 98 | unsigned int tlen; 99 | int remain; 100 | struct asignify_public_data *res; 101 | 102 | p = buf + sizeof(SSH_KEY_MAGIC) - 1; 103 | 104 | if (*p != ' ') { 105 | return (false); 106 | } 107 | 108 | p ++; 109 | 110 | if ((remain = b64_pton_stop(p, pkbuf, sizeof(pkbuf), " ")) <= 0) { 111 | return (false); 112 | } 113 | 114 | bpos = pkbuf; 115 | if ((tok = asignify_ssh_read_string(bpos, &tlen, remain, &bpos)) == NULL) { 116 | return (false); 117 | } 118 | 119 | /* First of all the algorithm name is encoded */ 120 | if (tlen != sizeof(SSH_KEY_MAGIC) - 1 || memcmp(tok, SSH_KEY_MAGIC, 121 | sizeof(SSH_KEY_MAGIC) - 1) != 0) { 122 | /* Wrong blob */ 123 | return (false); 124 | } 125 | 126 | remain -= tlen + 4; 127 | 128 | if ((tok = asignify_ssh_read_string(bpos, &tlen, remain, &bpos)) == NULL) { 129 | return (false); 130 | } 131 | 132 | if (tlen != crypto_sign_ed25519_PUBLICKEYBYTES) { 133 | return (true); 134 | } 135 | 136 | res = xmalloc(sizeof(*res)); 137 | res->version = 1; 138 | res->data_len = crypto_sign_ed25519_PUBLICKEYBYTES; 139 | res->id_len = 0; 140 | asignify_alloc_public_data_fields(res); 141 | memcpy(res->data, tok, res->data_len); 142 | 143 | *pk = res; 144 | 145 | return (true); 146 | } 147 | 148 | struct asignify_public_data* 149 | asignify_pubkey_load(FILE *f) 150 | { 151 | struct asignify_public_data *res = NULL; 152 | char *buf = NULL; 153 | size_t buflen = 0; 154 | ssize_t r; 155 | bool first = true; 156 | 157 | if (f == NULL) { 158 | abort(); 159 | } 160 | 161 | while ((r = getline(&buf, &buflen, f)) != -1) { 162 | if (r > sizeof(PUBKEY_MAGIC)) { 163 | if (first && memcmp(buf, PUBKEY_MAGIC, sizeof(PUBKEY_MAGIC) - 1) == 0) { 164 | res = asignify_public_data_load(buf, r, 165 | PUBKEY_MAGIC, sizeof(PUBKEY_MAGIC) - 1, 166 | PUBKEY_VER_MAX, PUBKEY_VER_MAX, 167 | KEY_ID_LEN, PUBKEY_KEY_LEN); 168 | /* XXX: should we stop after the first public key read? */ 169 | break; 170 | } 171 | } 172 | if (r > sizeof(SSH_KEY_MAGIC) && 173 | memcmp(buf, SSH_KEY_MAGIC, sizeof (SSH_KEY_MAGIC) - 1) == 0) { 174 | /* Try ssh pubkey */ 175 | if (asignify_pubkey_try_ssh(buf, r, &res)) { 176 | /* XXX: need to read all SSH keys */ 177 | break; 178 | } 179 | } 180 | else if (!asignify_pubkey_try_obsd(buf, r, &res)) { 181 | break; 182 | } 183 | 184 | first = false; 185 | } 186 | 187 | return (res); 188 | } 189 | 190 | bool 191 | asignify_pubkey_check_signature(struct asignify_public_data *pk, 192 | struct asignify_public_data *sig, const unsigned char *data, size_t dlen) 193 | { 194 | SHA2_CTX sh; 195 | unsigned char h[crypto_sign_HASHBYTES]; 196 | 197 | if (pk == NULL || sig == NULL) { 198 | return (false); 199 | } 200 | 201 | /* Check sanity */ 202 | if (pk->version != sig->version || 203 | pk->id_len != sig->id_len || 204 | (pk->id_len > 0 && memcmp(pk->id, sig->id, sig->id_len) != 0)) { 205 | return (false); 206 | } 207 | 208 | assert(pk->version == sig->version); 209 | 210 | switch (pk->version) { 211 | case 0: 212 | if (pk->data_len != crypto_sign_PUBLICKEYBYTES || 213 | sig->data_len != crypto_sign_BYTES) { 214 | break; 215 | } 216 | 217 | SHA512Init(&sh); 218 | SHA512Update(&sh, sig->data, 32); 219 | SHA512Update(&sh, pk->data, 32); 220 | SHA512Update(&sh, data, dlen); 221 | SHA512Final(h, &sh); 222 | 223 | if (crypto_sign_ed25519_verify_detached(sig->data, h, pk->data) == 0) { 224 | return (true); 225 | } 226 | break; 227 | case 1: 228 | if (pk->data_len != crypto_sign_PUBLICKEYBYTES || 229 | sig->data_len != crypto_sign_BYTES) { 230 | break; 231 | } 232 | 233 | /* ED25519 */ 234 | SHA512Init(&sh); 235 | SHA512Update(&sh, sig->data, 32); 236 | SHA512Update(&sh, pk->data, 32); 237 | SHA512Update(&sh, (const uint8_t *)&sig->version, 238 | sizeof(sig->version)); 239 | SHA512Update(&sh, data, dlen); 240 | SHA512Final(h, &sh); 241 | 242 | if (crypto_sign_ed25519_verify_detached(sig->data, h, pk->data) == 0) { 243 | return (true); 244 | } 245 | 246 | break; 247 | default: 248 | break; 249 | } 250 | 251 | return (false); 252 | } 253 | 254 | bool 255 | asignify_pubkey_write(struct asignify_public_data *pk, FILE *f) 256 | { 257 | char *b64data, *b64id; 258 | bool ret = false; 259 | 260 | if (pk == NULL || f == NULL) { 261 | return (false); 262 | } 263 | 264 | if (pk->version == 0) { 265 | /* XXX: support openbsd pubkeys format */ 266 | return (false); 267 | } 268 | if (pk->version != 1) { 269 | /* Future formats . */ 270 | return (false); 271 | } 272 | 273 | b64id = xmalloc(pk->id_len * 2); 274 | b64_ntop(pk->id, pk->id_len, b64id, pk->id_len * 2); 275 | b64data = xmalloc(pk->data_len * 2); 276 | b64_ntop(pk->data, pk->data_len, b64data, pk->data_len * 2); 277 | ret = (fprintf(f, "%s1:%s:%s\n", PUBKEY_MAGIC, b64id, b64data) > 0); 278 | free(b64id); 279 | free(b64data); 280 | 281 | return (ret); 282 | } 283 | -------------------------------------------------------------------------------- /libasignify/sha2.h: -------------------------------------------------------------------------------- 1 | /* $OpenBSD: sha2.h,v 1.9 2013/04/15 15:54:17 millert Exp $ */ 2 | 3 | /* 4 | * FILE: sha2.h 5 | * AUTHOR: Aaron D. Gifford 6 | * 7 | * Copyright (c) 2000-2001, Aaron D. Gifford 8 | * All rights reserved. 9 | * 10 | * Redistribution and use in source and binary forms, with or without 11 | * modification, are permitted provided that the following conditions 12 | * are met: 13 | * 1. Redistributions of source code must retain the above copyright 14 | * notice, this list of conditions and the following disclaimer. 15 | * 2. Redistributions in binary form must reproduce the above copyright 16 | * notice, this list of conditions and the following disclaimer in the 17 | * documentation and/or other materials provided with the distribution. 18 | * 3. Neither the name of the copyright holder nor the names of contributors 19 | * may be used to endorse or promote products derived from this software 20 | * without specific prior written permission. 21 | * 22 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTOR(S) ``AS IS'' AND 23 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTOR(S) BE LIABLE 26 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 | * SUCH DAMAGE. 33 | * 34 | * $From: sha2.h,v 1.1 2001/11/08 00:02:01 adg Exp adg $ 35 | */ 36 | 37 | #ifndef _SHA2_H 38 | #define _SHA2_H 39 | 40 | #include 41 | #include 42 | 43 | /*** SHA-256/384/512 Various Length Definitions ***********************/ 44 | #define SHA256_BLOCK_LENGTH 64 45 | #define SHA256_DIGEST_LENGTH 32 46 | #define SHA256_DIGEST_STRING_LENGTH (SHA256_DIGEST_LENGTH * 2 + 1) 47 | #define SHA512_BLOCK_LENGTH 128 48 | #define SHA512_DIGEST_LENGTH 64 49 | #define SHA512_DIGEST_STRING_LENGTH (SHA512_DIGEST_LENGTH * 2 + 1) 50 | 51 | 52 | /*** SHA-224/256/384/512 Context Structure *******************************/ 53 | typedef struct _SHA2_CTX { 54 | union { 55 | uint32_t st32[8]; 56 | uint64_t st64[8]; 57 | } state; 58 | uint64_t bitcount[2]; 59 | uint8_t buffer[SHA512_BLOCK_LENGTH]; 60 | } SHA2_CTX; 61 | 62 | __BEGIN_DECLS 63 | void SHA256Init(SHA2_CTX *); 64 | void SHA256Transform(uint32_t state[8], const uint8_t [SHA256_BLOCK_LENGTH]); 65 | void SHA256Update(SHA2_CTX *, const uint8_t *, size_t); 66 | void SHA256Pad(SHA2_CTX *); 67 | void SHA256Final(uint8_t [SHA256_DIGEST_LENGTH], SHA2_CTX *); 68 | char *SHA256End(SHA2_CTX *, char *); 69 | char *SHA256File(const char *, char *); 70 | char *SHA256FileChunk(const char *, char *, off_t, off_t); 71 | char *SHA256Data(const uint8_t *, size_t, char *); 72 | 73 | void SHA512Init(SHA2_CTX *); 74 | void SHA512Transform(uint64_t state[8], const uint8_t [SHA512_BLOCK_LENGTH]); 75 | void SHA512Update(SHA2_CTX *, const uint8_t *, size_t); 76 | void SHA512Pad(SHA2_CTX *); 77 | void SHA512Final(uint8_t [SHA512_DIGEST_LENGTH], SHA2_CTX *); 78 | char *SHA512End(SHA2_CTX *, char *); 79 | char *SHA512File(const char *, char *); 80 | char *SHA512FileChunk(const char *, char *, off_t, off_t); 81 | char *SHA512Data(const uint8_t *, size_t, char *); 82 | __END_DECLS 83 | 84 | #endif /* _SHA2_H */ 85 | -------------------------------------------------------------------------------- /libasignify/sign.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2015, Vsevolod Stakhov 2 | * All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are met: 6 | * * Redistributions of source code must retain the above copyright 7 | * notice, this list of conditions and the following disclaimer. 8 | * * Redistributions in binary form must reproduce the above copyright 9 | * notice, this list of conditions and the following disclaimer in the 10 | * documentation and/or other materials provided with the distribution. 11 | * 12 | * THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY 13 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 14 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 15 | * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY 16 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 17 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 18 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 19 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 20 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 21 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 22 | */ 23 | 24 | #ifdef HAVE_CONFIG_H 25 | #include "config.h" 26 | #endif 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | #include "blake2.h" 39 | #include "sha2.h" 40 | #include "asignify.h" 41 | #include "asignify_internal.h" 42 | #include "tweetnacl.h" 43 | #include "khash.h" 44 | #include "kvec.h" 45 | 46 | struct asignify_sign_ctx { 47 | struct asignify_private_data *privk; 48 | kvec_t(struct asignify_file) files; 49 | const char *error; 50 | }; 51 | 52 | asignify_sign_t* 53 | asignify_sign_init(void) 54 | { 55 | asignify_sign_t *nctx; 56 | 57 | nctx = xmalloc0(sizeof(*nctx)); 58 | 59 | return (nctx); 60 | } 61 | 62 | bool 63 | asignify_sign_load_privkey(asignify_sign_t *ctx, const char *privf, 64 | asignify_password_cb password_cb, void *d) 65 | { 66 | FILE *f; 67 | bool ret = false; 68 | int error = ASIGNIFY_ERROR_FORMAT; 69 | 70 | if (ctx == NULL || privf == NULL) { 71 | CTX_MAYBE_SET_ERR(ctx, ASIGNIFY_ERROR_MISUSE); 72 | return (false); 73 | } 74 | 75 | f = xfopen(privf, "r"); 76 | if (f == NULL) { 77 | ctx->error = xerr_string(ASIGNIFY_ERROR_FILE); 78 | return (false); 79 | } 80 | 81 | ctx->privk = asignify_private_data_load(f, &error, password_cb, d); 82 | if (ctx->privk == NULL) { 83 | ctx->error = xerr_string(error); 84 | goto cleanup; 85 | } 86 | 87 | ret = true; 88 | cleanup: 89 | fclose(f); 90 | 91 | return (ret); 92 | } 93 | 94 | bool 95 | asignify_sign_add_file(asignify_sign_t *ctx, const char *f, 96 | enum asignify_digest_type dt) 97 | { 98 | int fd; 99 | struct stat st; 100 | unsigned char *calc_digest; 101 | struct asignify_file check_file; 102 | struct asignify_file_digest *dig; 103 | bool ret; 104 | 105 | if (ctx == NULL || f == NULL || dt >= ASIGNIFY_DIGEST_MAX) { 106 | CTX_MAYBE_SET_ERR(ctx, ASIGNIFY_ERROR_MISUSE); 107 | return (false); 108 | } 109 | 110 | fd = xopen(f, O_RDONLY, 0); 111 | if (fd == -1) { 112 | ctx->error = xerr_string(ASIGNIFY_ERROR_FILE); 113 | return (false); 114 | } 115 | 116 | ret = false; 117 | check_file.fname = xstrdup(f); 118 | 119 | if (dt == ASIGNIFY_DIGEST_SIZE) { 120 | fstat(fd, &st); 121 | check_file.size = st.st_size; 122 | check_file.digests = 0; 123 | } 124 | else { 125 | calc_digest = asignify_digest_fd(dt, fd); 126 | 127 | if (calc_digest == NULL) { 128 | ctx->error = xerr_string(ASIGNIFY_ERROR_SIZE); 129 | goto cleanup; 130 | } 131 | 132 | dig = xmalloc0(sizeof(*dig)); 133 | dig->digest_type = dt; 134 | dig->digest = calc_digest; 135 | check_file.size = 0; 136 | check_file.digests = dig; 137 | } 138 | 139 | ret = true; 140 | kv_push(struct asignify_file, ctx->files, check_file); 141 | cleanup: 142 | close(fd); 143 | 144 | return (ret); 145 | } 146 | 147 | bool 148 | asignify_sign_write_signature(asignify_sign_t *ctx, const char *sigf) 149 | { 150 | kvec_t(char) out; 151 | char sig_pad[crypto_sign_BYTES + sizeof(unsigned int)]; 152 | char line[PATH_MAX + 256], hex[256]; 153 | struct asignify_file *f; 154 | int i, r; 155 | bool ret = false; 156 | struct asignify_public_data *sig = NULL; 157 | FILE *outf; 158 | 159 | if (ctx == NULL || ctx->privk == NULL || kv_size(ctx->files) == 0) { 160 | CTX_MAYBE_SET_ERR(ctx, ASIGNIFY_ERROR_MISUSE); 161 | return (false); 162 | } 163 | 164 | kv_init(out); 165 | kv_reserve(char, out, kv_size(ctx->files) * PATH_MAX + crypto_sign_BYTES); 166 | 167 | memset(sig_pad, 0, sizeof(sig_pad)); 168 | memcpy(sig_pad + crypto_sign_BYTES, &ctx->privk->version, 169 | sizeof(unsigned int)); 170 | kv_push_a(char, out, sig_pad, sizeof(sig_pad)); 171 | 172 | for (i = 0; i < kv_size(ctx->files); i ++) { 173 | f = &kv_A(ctx->files, i); 174 | if (f->size != 0) { 175 | r = snprintf(line, sizeof(line), "SIZE (%s) = %zu\n", f->fname, 176 | f->size); 177 | if (r >= sizeof(line)) { 178 | ctx->error = xerr_string(ASIGNIFY_ERROR_SIZE); 179 | goto cleanup; 180 | } 181 | } 182 | else { 183 | bin2hex(hex, sizeof(hex) - 1, f->digests->digest, 184 | asignify_digest_len(f->digests->digest_type)); 185 | r = snprintf(line, sizeof(line), "%s (%s) = %s\n", 186 | asignify_digest_name(f->digests->digest_type), 187 | f->fname, 188 | hex); 189 | if (r >= sizeof(line)) { 190 | ctx->error = xerr_string(ASIGNIFY_ERROR_SIZE); 191 | goto cleanup; 192 | } 193 | } 194 | kv_push_a(char, out, line, r); 195 | } 196 | 197 | sig = asignify_private_data_sign(ctx->privk, (unsigned char *)out.a, 198 | kv_size(out)); 199 | 200 | if (sig == NULL) { 201 | ctx->error = xerr_string(ASIGNIFY_ERROR_MISUSE); 202 | goto cleanup; 203 | } 204 | 205 | outf = xfopen(sigf, "w"); 206 | if (outf == NULL) { 207 | ctx->error = xerr_string(ASIGNIFY_ERROR_FILE); 208 | goto cleanup; 209 | } 210 | 211 | ret = asignify_signature_write(sig, out.a + sizeof(sig_pad), 212 | kv_size(out) - sizeof(sig_pad), outf); 213 | fclose(outf); 214 | 215 | cleanup: 216 | kv_destroy(out); 217 | 218 | return (ret); 219 | } 220 | 221 | const char* 222 | asignify_sign_get_error(asignify_sign_t *ctx) 223 | { 224 | if (ctx == NULL) { 225 | return (xerr_string(ASIGNIFY_ERROR_MISUSE)); 226 | } 227 | 228 | return (ctx->error); 229 | } 230 | 231 | void 232 | asignify_sign_free(asignify_sign_t *ctx) 233 | { 234 | struct asignify_file *f; 235 | int i; 236 | 237 | if (ctx) { 238 | asignify_private_data_free(ctx->privk); 239 | 240 | for (i = 0; i < kv_size(ctx->files); i ++) { 241 | f = &kv_A(ctx->files, i); 242 | if (f->digests) { 243 | free(f->digests->digest); 244 | free(f->digests); 245 | } 246 | free(f->fname); 247 | } 248 | kv_destroy(ctx->files); 249 | free(ctx); 250 | } 251 | } 252 | -------------------------------------------------------------------------------- /libasignify/signature.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015, Vsevolod Stakhov 3 | * 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * * Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY AUTHOR ''AS IS'' AND ANY 15 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY 18 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | */ 25 | 26 | #ifdef HAVE_CONFIG_H 27 | #include "config.h" 28 | #endif 29 | 30 | #include 31 | #include 32 | #include 33 | 34 | #include "asignify.h" 35 | #include "asignify_internal.h" 36 | #include "tweetnacl.h" 37 | 38 | #define SIG_MAGIC "asignify-sig:" 39 | #define OBSD_SIGALG "Ed" 40 | #define SIG_VER_MAX 1 41 | #define SIG_LEN crypto_sign_ed25519_BYTES 42 | 43 | struct obsd_signature { 44 | uint8_t sigalg[2]; 45 | uint8_t keynum[8]; 46 | uint8_t sig[crypto_sign_ed25519_BYTES]; 47 | }; 48 | 49 | 50 | static bool 51 | asignify_sig_try_obsd(const char *buf, size_t buflen, 52 | struct asignify_public_data **sig) 53 | { 54 | struct obsd_signature osig; 55 | struct asignify_public_data *res; 56 | 57 | if (buflen >= sizeof(OBSD_COMMENTHDR) - 1 && 58 | memcmp(buf, OBSD_COMMENTHDR, sizeof(OBSD_COMMENTHDR) - 1) == 0) { 59 | /* 60 | * XXX: 61 | * For now we do not use openbsd hints about keys for this specific 62 | * signature. 63 | */ 64 | return (true); 65 | } 66 | 67 | if (b64_pton(buf, (unsigned char *)&osig, sizeof(osig)) != sizeof(osig)) { 68 | return (false); 69 | } 70 | 71 | if (memcmp(osig.sigalg, OBSD_SIGALG, sizeof(osig.sigalg)) != 0) { 72 | return (false); 73 | } 74 | 75 | res = xmalloc0(sizeof(*res)); 76 | /* OpenBSD version code */ 77 | res->version = 0; 78 | res->data_len = sizeof(osig.sig); 79 | res->id_len = sizeof(osig.keynum); 80 | asignify_alloc_public_data_fields(res); 81 | memcpy(res->data, osig.sig, res->data_len); 82 | memcpy(res->id, osig.keynum, res->id_len); 83 | 84 | *sig = res; 85 | 86 | return (false); 87 | } 88 | 89 | struct asignify_public_data* 90 | asignify_signature_load(FILE *f, struct asignify_public_data *pk) 91 | { 92 | struct asignify_public_data *res = NULL; 93 | char *buf = NULL; 94 | size_t buflen = 0; 95 | ssize_t r; 96 | 97 | if (f == NULL) { 98 | abort(); 99 | } 100 | 101 | if ((r = getline(&buf, &buflen, f)) == -1) { 102 | return (NULL); 103 | } 104 | 105 | if (r > sizeof(SIG_MAGIC) && memcmp(buf, SIG_MAGIC, sizeof(SIG_MAGIC) - 1) == 0) { 106 | return (asignify_public_data_load(buf, r, 107 | SIG_MAGIC, sizeof(SIG_MAGIC) - 1, 108 | SIG_VER_MAX, SIG_VER_MAX, 109 | pk->id_len, SIG_LEN)); 110 | } else if (!asignify_sig_try_obsd(buf, r, &res)) { 111 | return (res); 112 | } 113 | 114 | while ((r = getline(&buf, &buflen, f)) != -1 && 115 | asignify_sig_try_obsd(buf, r, &res)) { 116 | /* More lines, please. */ 117 | } 118 | 119 | return (res); 120 | } 121 | 122 | struct asignify_public_data* 123 | asignify_private_data_sign(struct asignify_private_data *privk, 124 | unsigned char *buf, size_t len) 125 | { 126 | struct asignify_public_data *res; 127 | unsigned long long outlen = len; 128 | 129 | if (buf == NULL || privk == NULL || len == 0) 130 | return (NULL); 131 | 132 | res = xmalloc0(sizeof(*res)); 133 | res->version = privk->version; 134 | res->id_len = privk->id_len; 135 | res->data_len = crypto_sign_BYTES; 136 | 137 | asignify_alloc_public_data_fields(res); 138 | 139 | if (privk->id_len > 0) { 140 | memcpy(res->id, privk->id, res->id_len); 141 | } 142 | 143 | crypto_sign(buf, &outlen, buf + crypto_sign_BYTES, len - crypto_sign_BYTES, 144 | privk->data); 145 | memcpy(res->data, buf, res->data_len); 146 | 147 | return (res); 148 | } 149 | 150 | bool 151 | asignify_signature_write(struct asignify_public_data *sig, const void *buf, 152 | size_t len, FILE *f) 153 | { 154 | char *b64data, *b64id = NULL; 155 | bool ret = false; 156 | 157 | if (sig == NULL || f == NULL || buf == NULL) { 158 | return (false); 159 | } 160 | 161 | if (sig->version == 0) { 162 | /* XXX: support openbsd signatures format */ 163 | return (false); 164 | } else if (sig->version != 1) { 165 | /* Future formats. */ 166 | return (false); 167 | } 168 | 169 | if (sig->id_len > 0) { 170 | b64id = xmalloc(sig->id_len * 2); 171 | b64_ntop(sig->id, sig->id_len, b64id, sig->id_len * 2); 172 | } 173 | 174 | b64data = xmalloc(sig->data_len * 2); 175 | b64_ntop(sig->data, sig->data_len, b64data, sig->data_len * 2); 176 | 177 | if (b64id != NULL) { 178 | ret = (fprintf(f, "%s1:%s:%s\n", SIG_MAGIC, b64id, b64data) > 0); 179 | free(b64id); 180 | } 181 | else { 182 | ret = (fprintf(f, "%s1::%s\n", SIG_MAGIC, b64data) > 0); 183 | } 184 | free(b64data); 185 | 186 | if (ret) { 187 | ret = (fwrite(buf, len, 1, f) > 0); 188 | } 189 | 190 | return (ret); 191 | } 192 | -------------------------------------------------------------------------------- /libasignify/util.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2015, Vsevolod Stakhov 2 | * All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are met: 6 | * * Redistributions of source code must retain the above copyright 7 | * notice, this list of conditions and the following disclaimer. 8 | * * Redistributions in binary form must reproduce the above copyright 9 | * notice, this list of conditions and the following disclaimer in the 10 | * documentation and/or other materials provided with the distribution. 11 | * 12 | * THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY 13 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 14 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 15 | * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY 16 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 17 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 18 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 19 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 20 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 21 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 22 | */ 23 | 24 | #ifdef HAVE_CONFIG_H 25 | #include "config.h" 26 | #endif 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | #ifdef HAVE_OPENSSL 39 | #include 40 | #include 41 | #endif 42 | #ifdef HAVE_BSD_STDLIB_H 43 | #include 44 | #endif 45 | #ifdef HAVE_GETRANDOM 46 | #ifdef HAVE_LINUX_RANDOM_H 47 | #include 48 | #else 49 | #include 50 | #endif 51 | #endif 52 | 53 | #include "sha2.h" 54 | #include "blake2.h" 55 | 56 | #include "asignify_internal.h" 57 | 58 | static const unsigned int digest_lens[] = { 59 | [ASIGNIFY_DIGEST_SHA512] = SHA512_DIGEST_LENGTH, 60 | [ASIGNIFY_DIGEST_SHA256] = SHA256_DIGEST_LENGTH, 61 | [ASIGNIFY_DIGEST_BLAKE2] = BLAKE2B_OUTBYTES, 62 | }; 63 | 64 | static const char *digest_names[] = { 65 | [ASIGNIFY_DIGEST_SHA512] = "SHA512", 66 | [ASIGNIFY_DIGEST_SHA256] = "SHA256", 67 | [ASIGNIFY_DIGEST_BLAKE2] = "BLAKE2", 68 | [ASIGNIFY_DIGEST_SIZE] = "SIZE", 69 | }; 70 | 71 | const char* err_str[ASIGNIFY_ERROR_MAX] = { 72 | [ASIGNIFY_ERROR_OK] = "no error", 73 | [ASIGNIFY_ERROR_FILE] = "file IO error", 74 | [ASIGNIFY_ERROR_FORMAT] = "incorrect data format", 75 | [ASIGNIFY_ERROR_PASSWORD] = "wrong password", 76 | [ASIGNIFY_ERROR_MISUSE] = "library is used incorrectly", 77 | [ASIGNIFY_ERROR_VERIFY_SIZE] = "incorrect file size", 78 | [ASIGNIFY_ERROR_VERIFY] = "signature verification error", 79 | [ASIGNIFY_ERROR_VERIFY_DIGEST] = "digest verification error", 80 | [ASIGNIFY_ERROR_NO_DIGEST] = "digest is missing for the file specified", 81 | [ASIGNIFY_ERROR_WRONG_KEYPAIR] = "cannot encrypt using related keypair", 82 | [ASIGNIFY_ERROR_WRONG_KEY] = "wrong key specified", 83 | [ASIGNIFY_ERROR_SIZE] = "size mismatch" 84 | }; 85 | 86 | #ifdef HAVE_WEAK_SYMBOLS 87 | __attribute__((weak)) void 88 | _dummy_symbol_to_prevent_lto(void * const pnt, const size_t len) 89 | { 90 | (void) pnt; 91 | (void) len; 92 | } 93 | #endif 94 | 95 | void explicit_memzero(void * const pnt, const size_t len) 96 | { 97 | #if defined(HAVE_MEMSET_S) 98 | if (memset_s(pnt, (rsize_t) len, 0, (rsize_t) len) != 0) { 99 | abort(); 100 | } 101 | #elif defined(HAVE_EXPLICIT_BZERO) 102 | explicit_bzero(pnt, len); 103 | #elif HAVE_WEAK_SYMBOLS 104 | memset(pnt, 0, len); 105 | _dummy_symbol_to_prevent_lto(pnt, len); 106 | #else 107 | volatile unsigned char *pnt_ = (volatile unsigned char *) pnt; 108 | size_t i = (size_t) 0U; 109 | while (i < len) { 110 | pnt_[i++] = 0U; 111 | } 112 | #endif 113 | } 114 | 115 | 116 | void 117 | randombytes(unsigned char *buf, uint64_t len) 118 | { 119 | #ifdef HAVE_GETRANDOM 120 | if (getrandom(buf, len, 0) == -1) { 121 | abort(); 122 | } 123 | #elif defined(HAVE_ARC4RANDOM_BUF) 124 | arc4random_buf(buf, len); 125 | #elif defined(HAVE_OPENSSL) 126 | if (RAND_bytes(buf, len) != 1) { 127 | abort(); 128 | } 129 | #else 130 | int fd; 131 | struct stat st; 132 | 133 | fd = open("/dev/urandom", O_RDONLY); 134 | 135 | if (fd == -1 || fstat(fd, &st) == -1 || !S_ISCHR(st.st_mode)) { 136 | abort(); 137 | } 138 | 139 | if (read(fd, buf, len) != len) { 140 | abort(); 141 | } 142 | 143 | close(fd); 144 | #endif 145 | } 146 | 147 | 148 | FILE * 149 | xfopen(const char *fname, const char *mode) 150 | { 151 | struct stat sb; 152 | FILE *res = NULL; 153 | 154 | if (fname == NULL || mode == NULL) { 155 | return (NULL); 156 | } 157 | 158 | if (strcmp(fname, "-") == 0) { 159 | if (strchr(mode, 'w') != NULL) { 160 | return (stdout); 161 | } 162 | else { 163 | return (stdin); 164 | } 165 | } 166 | else { 167 | if (stat(fname, &sb) == -1) { 168 | if (strchr(mode, 'w') != NULL) { 169 | res = fopen(fname, mode); 170 | } 171 | else { 172 | return (NULL); 173 | } 174 | } 175 | else if (S_ISDIR(sb.st_mode)) { 176 | errno = EINVAL; 177 | } 178 | else { 179 | res = fopen(fname, mode); 180 | } 181 | } 182 | 183 | return (res); 184 | } 185 | 186 | int 187 | xopen(const char *fname, int oflags, mode_t mode) 188 | { 189 | struct stat sb; 190 | int fd; 191 | 192 | if (strcmp(fname, "-") == 0) { 193 | if ((oflags & O_WRONLY)) { 194 | fd = dup(STDOUT_FILENO); 195 | } 196 | else { 197 | fd = dup(STDIN_FILENO); 198 | } 199 | if (fd == -1) { 200 | return (-1); 201 | } 202 | } 203 | else { 204 | #ifdef HAVE_O_NOFOLLOW 205 | fd = open(fname, oflags | O_NOFOLLOW, mode); 206 | #else 207 | fd = open(fname, oflags, mode); 208 | #endif 209 | 210 | if (fd == -1) { 211 | return (-1); 212 | } 213 | } 214 | 215 | if (fstat(fd, &sb) == -1 || S_ISDIR(sb.st_mode)) { 216 | close(fd); 217 | return (-1); 218 | } 219 | 220 | return (fd); 221 | } 222 | 223 | void * 224 | xmalloc(size_t len) 225 | { 226 | void *p; 227 | 228 | if (len >= SIZE_MAX / 2) { 229 | abort(); 230 | } 231 | 232 | if (!(p = malloc(len))) { 233 | abort(); 234 | } 235 | return (p); 236 | } 237 | 238 | void * 239 | xmalloc0(size_t len) 240 | { 241 | void *p = xmalloc(len); 242 | 243 | memset(p, 0, len); 244 | 245 | return (p); 246 | } 247 | 248 | void * 249 | xmalloc_aligned(size_t align, size_t len) 250 | { 251 | void *p; 252 | unsigned int v = (unsigned int)len; 253 | 254 | v--; 255 | v |= v >> 1; 256 | v |= v >> 2; 257 | v |= v >> 4; 258 | v |= v >> 8; 259 | v |= v >> 16; 260 | v++; 261 | 262 | if (align > len || len >= UINT32_MAX / 2 || (v & (v - 1)) != 0) { 263 | abort(); 264 | } 265 | 266 | #ifdef HAVE_POSIX_MEMALIGN 267 | if ((posix_memalign(&p, align, v))) { 268 | abort(); 269 | } 270 | #elif defined(HAVE_ALIGNED_ALLOC) 271 | if (!(p = aligned_alloc(align, v))) { 272 | abort(); 273 | } 274 | #else 275 | #warning No aligned alloc function found 276 | if (!(p = malloc(v))) { 277 | abort(); 278 | } 279 | #endif 280 | return (p); 281 | } 282 | 283 | char * 284 | xstrdup(const char *str) 285 | { 286 | char *p; 287 | 288 | if (!(p = strdup(str))) { 289 | abort(); 290 | } 291 | 292 | return (p); 293 | } 294 | 295 | const char * 296 | xerr_string(enum asignify_error code) 297 | { 298 | if (code < ASIGNIFY_ERROR_OK || code >= ASIGNIFY_ERROR_MAX) { 299 | return (NULL); 300 | } 301 | 302 | return (err_str[code]); 303 | } 304 | 305 | /* Derived from original code by CodesInChaos */ 306 | 307 | int 308 | hex2bin(unsigned char * const bin, const size_t bin_maxlen, 309 | const char * const hex, const size_t hex_len, 310 | size_t * const bin_len, const char ** const hex_end) 311 | { 312 | size_t bin_pos = (size_t) 0U; 313 | size_t hex_pos = (size_t) 0U; 314 | int ret = 0; 315 | unsigned char c; 316 | unsigned char c_acc = 0U; 317 | unsigned char c_num; 318 | unsigned char c_val; 319 | unsigned char state = 0U; 320 | 321 | while (hex_pos < hex_len) { 322 | c = (unsigned char) hex[hex_pos]; 323 | if ((c_num = c ^ 48U) < 10U) { 324 | c_val = c_num; 325 | } 326 | else if ((c_num = (c & ~32U)) > 64 && c_num < 71U) { 327 | c_val = c_num - 55U; 328 | } 329 | else { 330 | break; 331 | } 332 | if (bin_pos >= bin_maxlen) { 333 | ret = -1; 334 | errno = ERANGE; 335 | break; 336 | } 337 | if (state == 0U) { 338 | c_acc = c_val * 16U; 339 | } 340 | else { 341 | bin[bin_pos++] = c_acc | c_val; 342 | } 343 | state = ~state; 344 | hex_pos++; 345 | } 346 | 347 | if (state != 0U) { 348 | hex_pos--; 349 | } 350 | 351 | if (hex_end != NULL) { 352 | *hex_end = &hex[hex_pos]; 353 | } 354 | 355 | if (bin_len != NULL) { 356 | *bin_len = bin_pos; 357 | } 358 | 359 | return ret; 360 | } 361 | 362 | char * 363 | bin2hex(char * const hex, const size_t hex_maxlen, 364 | const unsigned char * const bin, const size_t bin_len) 365 | { 366 | size_t i = (size_t) 0U; 367 | unsigned int x; 368 | int b; 369 | int c; 370 | 371 | if (bin_len >= SIZE_MAX / 2 || hex_maxlen < bin_len * 2U) { 372 | abort(); 373 | } 374 | while (i < bin_len) { 375 | c = bin[i] & 0xf; 376 | b = bin[i] >> 4; 377 | x = (unsigned char) (87 + c + (((c - 10) >> 31) & -39)) << 8 | 378 | (unsigned char) (87 + b + (((b - 10) >> 31) & -39)); 379 | hex[i * 2U] = (char) x; 380 | x >>= 8; 381 | hex[i * 2U + 1U] = (char) x; 382 | i++; 383 | } 384 | hex[i * 2U] = 0; 385 | 386 | return hex; 387 | } 388 | 389 | unsigned int 390 | asignify_digest_len(enum asignify_digest_type type) 391 | { 392 | 393 | if (type >= nitems(digest_lens)) { 394 | return (0); 395 | } 396 | return (digest_lens[type]); 397 | } 398 | 399 | const char * 400 | asignify_digest_name(enum asignify_digest_type type) 401 | { 402 | const char *ret; 403 | 404 | if (type >= nitems(digest_names) || (ret = digest_names[type]) == NULL) { 405 | return (""); 406 | } 407 | 408 | return (ret); 409 | } 410 | 411 | static void * 412 | asignify_digest_init(enum asignify_digest_type type) 413 | { 414 | #ifdef HAVE_OPENSSL 415 | EVP_MD_CTX *mdctx; 416 | #else 417 | SHA2_CTX *st; 418 | #endif 419 | blake2b_state *bst; 420 | 421 | void *res = NULL; 422 | 423 | switch(type) { 424 | case ASIGNIFY_DIGEST_SHA512: 425 | #ifdef HAVE_OPENSSL 426 | mdctx = EVP_MD_CTX_create(); 427 | EVP_DigestInit_ex(mdctx, EVP_sha512(), NULL); 428 | res = mdctx; 429 | #else 430 | st = xmalloc(sizeof(*st)); 431 | SHA512Init(st); 432 | res = st; 433 | #endif 434 | break; 435 | case ASIGNIFY_DIGEST_SHA256: 436 | #ifdef HAVE_OPENSSL 437 | mdctx = EVP_MD_CTX_create(); 438 | EVP_DigestInit(mdctx, EVP_sha256()); 439 | res = mdctx; 440 | #else 441 | st = xmalloc(sizeof(*st)); 442 | SHA256Init(st); 443 | res = st; 444 | #endif 445 | break; 446 | case ASIGNIFY_DIGEST_BLAKE2: 447 | bst = xmalloc_aligned(64, sizeof(*bst)); 448 | blake2b_init(bst, BLAKE2B_OUTBYTES); 449 | res = bst; 450 | break; 451 | default: 452 | abort(); 453 | break; 454 | } 455 | 456 | return (res); 457 | } 458 | 459 | static void 460 | asignify_digest_free(enum asignify_digest_type type, void *res) 461 | { 462 | 463 | switch (type) { 464 | case ASIGNIFY_DIGEST_BLAKE2: 465 | free(res); 466 | break; 467 | default: 468 | #ifdef HAVE_OPENSSL 469 | EVP_MD_CTX_free(res); 470 | #else 471 | free(res); 472 | #endif 473 | break; 474 | } 475 | } 476 | 477 | static void 478 | asignify_digest_update(enum asignify_digest_type type, void *ctx, 479 | const unsigned char *buf, size_t len) 480 | { 481 | #ifdef HAVE_OPENSSL 482 | EVP_MD_CTX *mdctx; 483 | #else 484 | SHA2_CTX *st; 485 | #endif 486 | blake2b_state *bst; 487 | 488 | switch(type) { 489 | case ASIGNIFY_DIGEST_SHA512: 490 | #ifdef HAVE_OPENSSL 491 | mdctx = (EVP_MD_CTX *)ctx; 492 | EVP_DigestUpdate(mdctx, buf, len); 493 | #else 494 | st = (SHA2_CTX *)ctx; 495 | SHA512Update(st, buf, len); 496 | #endif 497 | break; 498 | case ASIGNIFY_DIGEST_SHA256: 499 | #ifdef HAVE_OPENSSL 500 | mdctx = (EVP_MD_CTX *)ctx; 501 | EVP_DigestUpdate(mdctx, buf, len); 502 | #else 503 | st = (SHA2_CTX *)ctx; 504 | SHA256Update(st, buf, len); 505 | #endif 506 | break; 507 | case ASIGNIFY_DIGEST_BLAKE2: 508 | bst = (blake2b_state *)ctx; 509 | blake2b_update(bst, buf, len); 510 | break; 511 | default: 512 | abort(); 513 | break; 514 | } 515 | 516 | } 517 | 518 | static unsigned char* 519 | asignify_digest_final(enum asignify_digest_type type, void *ctx) 520 | { 521 | unsigned int len = asignify_digest_len(type); 522 | unsigned char *res; 523 | #ifdef HAVE_OPENSSL 524 | EVP_MD_CTX *mdctx; 525 | #else 526 | SHA2_CTX *st; 527 | #endif 528 | blake2b_state *bst; 529 | 530 | res = xmalloc(len); 531 | switch(type) { 532 | case ASIGNIFY_DIGEST_SHA512: 533 | #ifdef HAVE_OPENSSL 534 | mdctx = (EVP_MD_CTX *)ctx; 535 | EVP_DigestFinal(mdctx, res, &len); 536 | #else 537 | st = (SHA2_CTX *)ctx; 538 | SHA512Final(res, st); 539 | #endif 540 | break; 541 | case ASIGNIFY_DIGEST_SHA256: 542 | #ifdef HAVE_OPENSSL 543 | mdctx = (EVP_MD_CTX *)ctx; 544 | EVP_DigestFinal(mdctx, res, &len); 545 | #else 546 | st = (SHA2_CTX *)ctx; 547 | SHA256Final(res, st); 548 | #endif 549 | break; 550 | case ASIGNIFY_DIGEST_BLAKE2: 551 | bst = (blake2b_state *)ctx; 552 | blake2b_final(bst, res, len); 553 | break; 554 | default: 555 | abort(); 556 | break; 557 | } 558 | 559 | asignify_digest_free(type, ctx); 560 | return (res); 561 | } 562 | 563 | unsigned char* 564 | asignify_digest_fd(enum asignify_digest_type type, int fd) 565 | { 566 | int r; 567 | #if BUFSIZ >= 2048 568 | unsigned char buf[BUFSIZ]; 569 | #else 570 | /* BUFSIZ is insanely small */ 571 | unsigned char buf[4096]; 572 | #endif 573 | void *dgst; 574 | 575 | if (fd == -1 || type >= ASIGNIFY_DIGEST_SIZE || 576 | (dgst = asignify_digest_init(type)) == NULL) { 577 | return (NULL); 578 | } 579 | 580 | if (lseek(fd, 0, SEEK_SET) == (off_t)-1) { 581 | asignify_digest_free(type, dgst); 582 | return (NULL); 583 | } 584 | 585 | while ((r = read(fd, buf, sizeof(buf))) > 0) { 586 | asignify_digest_update(type, dgst, buf, r); 587 | } 588 | 589 | return (asignify_digest_final(type, dgst)); 590 | } 591 | -------------------------------------------------------------------------------- /libasignify/verify.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2015, Vsevolod Stakhov 2 | * All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are met: 6 | * * Redistributions of source code must retain the above copyright 7 | * notice, this list of conditions and the following disclaimer. 8 | * * Redistributions in binary form must reproduce the above copyright 9 | * notice, this list of conditions and the following disclaimer in the 10 | * documentation and/or other materials provided with the distribution. 11 | * 12 | * THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY 13 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 14 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 15 | * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY 16 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 17 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 18 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 19 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 20 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 21 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 22 | */ 23 | 24 | 25 | #ifdef HAVE_CONFIG_H 26 | #include "config.h" 27 | #endif 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | 39 | #include "blake2.h" 40 | #include "sha2.h" 41 | #include "asignify.h" 42 | #include "asignify_internal.h" 43 | #include "khash.h" 44 | #include "kvec.h" 45 | 46 | KHASH_INIT(asignify_verify_hnode, const char *, struct asignify_file *, 1, 47 | kh_str_hash_func, kh_str_hash_equal); 48 | 49 | #define DMAP_ENTRY(litname, dtype) \ 50 | { \ 51 | .name = litname, \ 52 | .type = dtype, \ 53 | .namelen = sizeof(litname) - 1 \ 54 | } 55 | 56 | static const struct digest_map_entry { 57 | const char *name; 58 | enum asignify_digest_type type; 59 | size_t namelen; 60 | } digest_map[] = { 61 | DMAP_ENTRY("sha512", ASIGNIFY_DIGEST_SHA512), 62 | DMAP_ENTRY("sha256", ASIGNIFY_DIGEST_SHA256), 63 | DMAP_ENTRY("blake2", ASIGNIFY_DIGEST_BLAKE2), 64 | DMAP_ENTRY("size", ASIGNIFY_DIGEST_SIZE), 65 | }; 66 | 67 | static const unsigned int digests_sizes[ASIGNIFY_DIGEST_MAX] = { 68 | [ASIGNIFY_DIGEST_SHA512] = SHA512_DIGEST_STRING_LENGTH - 1, 69 | [ASIGNIFY_DIGEST_SHA256] = SHA256_DIGEST_STRING_LENGTH - 1, 70 | [ASIGNIFY_DIGEST_BLAKE2] = BLAKE2B_OUTBYTES * 2, 71 | }; 72 | 73 | struct asignify_pubkey_chain { 74 | struct asignify_public_data *pk; 75 | struct asignify_pubkey_chain *next; 76 | }; 77 | 78 | struct asignify_verify_ctx { 79 | struct asignify_pubkey_chain *pk_chain; 80 | khash_t(asignify_verify_hnode) *files; 81 | const char *error; 82 | }; 83 | 84 | static unsigned char * 85 | asignify_verify_load_sig(struct asignify_verify_ctx *ctx, FILE *f, size_t *len) 86 | { 87 | const size_t maxlen = 1 << 30; 88 | struct stat st; 89 | int r; 90 | #if BUFSIZ >= 2048 91 | unsigned char buf[BUFSIZ]; 92 | #else 93 | /* BUFSIZ is insanely small */ 94 | unsigned char buf[4096]; 95 | #endif 96 | kvec_t(unsigned char) res; 97 | 98 | if (ctx == NULL || f == NULL || fstat(fileno(f), &st) == -1) { 99 | return (NULL); 100 | } 101 | 102 | if (S_ISREG(st.st_mode) && st.st_size > maxlen) { 103 | ctx->error = xerr_string(ASIGNIFY_ERROR_FILE); 104 | return (NULL); 105 | } 106 | 107 | kv_init(res); 108 | 109 | while ((r = fread(buf, 1, sizeof(buf), f)) > 0) { 110 | kv_push_a(unsigned char, res, buf, r); 111 | } 112 | 113 | *len = kv_size(res); 114 | kv_push(unsigned char, res, '\0'); 115 | 116 | return (res.a); 117 | } 118 | 119 | enum asignify_digest_type 120 | asignify_digest_from_str(const char *data, ssize_t dlen) 121 | { 122 | const struct digest_map_entry *entry; 123 | 124 | if (dlen <= 0) 125 | return (ASIGNIFY_DIGEST_MAX); 126 | for (size_t i = 0; i < nitems(digest_map); i++) { 127 | entry = &digest_map[i]; 128 | if ((size_t)dlen != entry->namelen) 129 | continue; 130 | if (strncasecmp(data, entry->name, entry->namelen) == 0) 131 | return (entry->type); 132 | } 133 | 134 | return (ASIGNIFY_DIGEST_MAX); 135 | } 136 | 137 | static bool 138 | asignify_verify_parse_digest(const char *data, ssize_t dlen, 139 | enum asignify_digest_type type, struct asignify_file *f) 140 | { 141 | char *errstr; 142 | uint64_t flen; 143 | struct asignify_file_digest *dig; 144 | unsigned int dig_len, dig_strlen; 145 | 146 | if (dlen <= 0 || f == NULL) { 147 | return (false); 148 | } 149 | 150 | if (type == ASIGNIFY_DIGEST_SIZE) { 151 | /* Special case for size */ 152 | errno = 0; 153 | flen = strtoumax (data, &errstr, 10); 154 | if (errstr != data + dlen || errno != 0) { 155 | return (false); 156 | } 157 | 158 | f->size = flen; 159 | return (true); 160 | } else if (type >= nitems(digests_sizes)) { 161 | return (false); 162 | } 163 | 164 | dig_strlen = digests_sizes[type]; 165 | if (dig_strlen == 0 || dig_strlen != dlen) { 166 | return (false); 167 | } 168 | 169 | dig = xmalloc(sizeof(*dig)); 170 | dig->digest_type = type; 171 | dig_len = asignify_digest_len(type); 172 | 173 | if (dig_len == 0) { 174 | free(dig); 175 | return (false); 176 | } 177 | 178 | dig->digest = xmalloc(dig_len); 179 | 180 | if (hex2bin(dig->digest, dig_len, data, dlen, NULL, NULL) != 0) { 181 | free(dig->digest); 182 | free(dig); 183 | return (false); 184 | } 185 | 186 | dig->next = f->digests; 187 | f->digests = dig; 188 | 189 | return (true); 190 | } 191 | 192 | static bool 193 | asignify_verify_parse_files(struct asignify_verify_ctx *ctx, const char *data, 194 | size_t dlen) 195 | { 196 | enum stm_st { 197 | PARSE_START = 0, 198 | PARSE_ALG, 199 | PARSE_OBRACE, 200 | PARSE_FILE, 201 | PARSE_EQSIGN, 202 | PARSE_HASH, 203 | PARSE_SPACES, 204 | } state = PARSE_START, next_state = PARSE_START; 205 | const unsigned char *p, *end, *c; 206 | char *fbuf; 207 | khiter_t k; 208 | int r; 209 | struct asignify_file *cur_file = NULL; 210 | enum asignify_digest_type dig_type = ASIGNIFY_DIGEST_MAX; 211 | 212 | p = (unsigned char *)data; 213 | end = p + dlen; 214 | c = p; 215 | 216 | while (p <= end) { 217 | switch (state) { 218 | case PARSE_START: 219 | cur_file = NULL; 220 | if (*p == '\0') { 221 | goto finished; 222 | } 223 | else if (isspace(*p)) { 224 | next_state = PARSE_START; 225 | state = PARSE_SPACES; 226 | } 227 | else { 228 | /* We have algorithm definition */ 229 | c = p; 230 | state = PARSE_ALG; 231 | } 232 | break; 233 | case PARSE_ALG: 234 | if (isgraph(*p)) { 235 | p ++; 236 | } 237 | else if (*p == ' ') { 238 | /* Check algorithm */ 239 | dig_type = asignify_digest_from_str((const char *)c, p - c); 240 | if (dig_type == ASIGNIFY_DIGEST_MAX) { 241 | goto parse_error; 242 | } 243 | 244 | state = PARSE_SPACES; 245 | next_state = PARSE_OBRACE; 246 | } 247 | else { 248 | goto parse_error; 249 | } 250 | break; 251 | case PARSE_OBRACE: 252 | if (*p != '(') { 253 | goto parse_error; 254 | } 255 | 256 | p++; 257 | c = p; 258 | state = PARSE_FILE; 259 | break; 260 | case PARSE_FILE: 261 | if (isgraph(*p) && *p != ')') { 262 | p ++; 263 | } 264 | else if (*p == ')') { 265 | /* Check file */ 266 | if (p - c > 0) { 267 | 268 | fbuf = xmalloc(p - c + 1); 269 | memcpy(fbuf, c, p - c); 270 | fbuf[p - c] = '\0'; 271 | k = kh_get(asignify_verify_hnode, ctx->files, fbuf); 272 | 273 | if (k != kh_end(ctx->files)) { 274 | /* We already have the node */ 275 | free(fbuf); 276 | fbuf = NULL; 277 | cur_file = kh_value(ctx->files, k); 278 | } 279 | else { 280 | cur_file = xmalloc0(sizeof(*cur_file)); 281 | cur_file->fname = fbuf; 282 | k = kh_put(asignify_verify_hnode, ctx->files, 283 | cur_file->fname, &r); 284 | fbuf = NULL; 285 | 286 | if (r == -1) { 287 | goto parse_error; 288 | } 289 | 290 | kh_value(ctx->files, k) = cur_file; 291 | } 292 | 293 | p ++; 294 | c = p; 295 | next_state = PARSE_EQSIGN; 296 | state = PARSE_SPACES; 297 | } 298 | else { 299 | goto parse_error; 300 | } 301 | } 302 | break; 303 | case PARSE_EQSIGN: 304 | if (*p != '=') { 305 | goto parse_error; 306 | } 307 | 308 | p++; 309 | c = p; 310 | state = PARSE_SPACES; 311 | next_state = PARSE_HASH; 312 | break; 313 | case PARSE_HASH: 314 | if (isxdigit(*p)) { 315 | p ++; 316 | } 317 | else if (*p == '\n' || *p == '\0') { 318 | if (!asignify_verify_parse_digest((const char *)c, p - c, 319 | dig_type, cur_file)) { 320 | goto parse_error; 321 | } 322 | 323 | state = PARSE_START; 324 | } 325 | break; 326 | case PARSE_SPACES: 327 | if (*p != '\0' && isspace(*p)) { 328 | p ++; 329 | } 330 | else { 331 | c = p; 332 | state = next_state; 333 | } 334 | break; 335 | } 336 | } 337 | 338 | parse_error: 339 | ctx->error = xerr_string(ASIGNIFY_ERROR_FORMAT); 340 | return (false); 341 | 342 | finished: 343 | if (p != end) { 344 | goto parse_error; 345 | } 346 | return (true); 347 | } 348 | 349 | asignify_verify_t* 350 | asignify_verify_init(void) 351 | { 352 | asignify_verify_t *nctx; 353 | 354 | nctx = xmalloc0(sizeof(*nctx)); 355 | 356 | return (nctx); 357 | } 358 | 359 | 360 | bool 361 | asignify_verify_load_pubkey(asignify_verify_t *ctx, const char *pubf) 362 | { 363 | FILE *f; 364 | bool ret = false; 365 | struct asignify_public_data *pk; 366 | struct asignify_pubkey_chain *chain; 367 | 368 | if (ctx == NULL) { 369 | return (false); 370 | } 371 | 372 | f = xfopen(pubf, "r"); 373 | if (f == NULL) { 374 | ctx->error = xerr_string(ASIGNIFY_ERROR_FILE); 375 | return (false); 376 | } 377 | 378 | pk = asignify_pubkey_load(f); 379 | if (pk == NULL) { 380 | ctx->error = xerr_string(ASIGNIFY_ERROR_FORMAT); 381 | goto cleanup; 382 | } 383 | 384 | ret = true; 385 | chain = xmalloc(sizeof(*chain)); 386 | chain->pk = pk; 387 | chain->next = ctx->pk_chain; 388 | ctx->pk_chain = chain; 389 | 390 | cleanup: 391 | fclose(f); 392 | 393 | return (ret); 394 | } 395 | 396 | bool 397 | asignify_verify_load_signature(asignify_verify_t *ctx, const char *sigf) 398 | { 399 | struct asignify_public_data *sig; 400 | struct asignify_pubkey_chain *chain; 401 | unsigned char *data; 402 | size_t dlen; 403 | FILE *f; 404 | bool ret = false; 405 | 406 | if (ctx == NULL || ctx->pk_chain == NULL) { 407 | CTX_MAYBE_SET_ERR(ctx, ASIGNIFY_ERROR_MISUSE); 408 | return (false); 409 | } 410 | 411 | sig = NULL; 412 | data = NULL; 413 | f = xfopen(sigf, "r"); 414 | if (f == NULL) { 415 | ctx->error = xerr_string(ASIGNIFY_ERROR_FILE); 416 | return (false); 417 | } 418 | 419 | /* XXX: we assume that all pk in chain are the same */ 420 | sig = asignify_signature_load(f, ctx->pk_chain->pk); 421 | if (ctx->pk_chain == NULL) { 422 | ctx->error = xerr_string(ASIGNIFY_ERROR_FORMAT); 423 | goto cleanup; 424 | } 425 | 426 | data = asignify_verify_load_sig(ctx, f, &dlen); 427 | if (data == NULL || dlen == 0) { 428 | /* XXX set ctx->error */ 429 | goto cleanup; 430 | } 431 | 432 | chain = ctx->pk_chain; 433 | while (chain != NULL && !ret) { 434 | ret = asignify_pubkey_check_signature(chain->pk, sig, data, dlen); 435 | chain = chain->next; 436 | } 437 | 438 | if (!ret) { 439 | ctx->error = xerr_string(ASIGNIFY_ERROR_VERIFY); 440 | goto cleanup; 441 | } 442 | 443 | /* We are now safe to parse digests */ 444 | ctx->files = kh_init(asignify_verify_hnode); 445 | 446 | ret = asignify_verify_parse_files(ctx, (const char *)data, dlen); 447 | 448 | cleanup: 449 | asignify_public_data_free(sig); 450 | free(data); 451 | if (f != NULL) 452 | fclose(f); 453 | return (ret); 454 | } 455 | 456 | bool 457 | asignify_verify_file(asignify_verify_t *ctx, const char *checkf) 458 | { 459 | khiter_t k; 460 | struct stat st; 461 | int fd; 462 | struct asignify_file *f; 463 | struct asignify_file_digest *d; 464 | unsigned char *calc_digest; 465 | bool equiv, ret; 466 | 467 | if (ctx == NULL || ctx->files == NULL) { 468 | CTX_MAYBE_SET_ERR(ctx, ASIGNIFY_ERROR_MISUSE); 469 | return (false); 470 | } 471 | 472 | k = kh_get(asignify_verify_hnode, ctx->files, checkf); 473 | if (k == kh_end(ctx->files)) { 474 | ctx->error = xerr_string(ASIGNIFY_ERROR_NO_DIGEST); 475 | return (false); 476 | } 477 | 478 | fd = xopen(checkf, O_RDONLY, 0); 479 | ret = false; 480 | 481 | f = kh_value(ctx->files, k); 482 | 483 | if (fstat(fd, &st) == -1 || S_ISDIR(st.st_mode)) { 484 | ctx->error = xerr_string(ASIGNIFY_ERROR_FILE); 485 | goto cleanup; 486 | } 487 | 488 | if (f->size > 0 && f->size != st.st_size) { 489 | ctx->error = xerr_string(ASIGNIFY_ERROR_VERIFY_SIZE); 490 | goto cleanup; 491 | } 492 | 493 | d = f->digests; 494 | while (d) { 495 | calc_digest = asignify_digest_fd(d->digest_type, fd); 496 | 497 | if (calc_digest == NULL) { 498 | ctx->error = xerr_string(ASIGNIFY_ERROR_SIZE); 499 | goto cleanup; 500 | } 501 | 502 | equiv = memcmp(calc_digest, d->digest, 503 | asignify_digest_len(d->digest_type)) == 0; 504 | free(calc_digest); 505 | 506 | if (!equiv) { 507 | ctx->error = xerr_string(ASIGNIFY_ERROR_VERIFY_DIGEST); 508 | goto cleanup; 509 | } 510 | d = d->next; 511 | } 512 | 513 | ret = true; 514 | cleanup: 515 | close(fd); 516 | return (ret); 517 | } 518 | 519 | 520 | const char* 521 | asignify_verify_get_error(asignify_verify_t *ctx) 522 | { 523 | if (ctx == NULL) { 524 | return (xerr_string(ASIGNIFY_ERROR_MISUSE)); 525 | } 526 | 527 | return (ctx->error); 528 | } 529 | 530 | void 531 | asignify_verify_free(asignify_verify_t *ctx) 532 | { 533 | khiter_t k; 534 | struct asignify_file_digest *d, *dtmp; 535 | struct asignify_file *f; 536 | struct asignify_pubkey_chain *chain, *ctmp; 537 | 538 | if (ctx == NULL) 539 | return; 540 | 541 | 542 | chain = ctx->pk_chain; 543 | while (chain != NULL) { 544 | asignify_public_data_free(chain->pk); 545 | ctmp = chain; 546 | chain = chain->next; 547 | free(ctmp); 548 | } 549 | 550 | if (ctx->files) { 551 | for (k = kh_begin(ctx->files); k != kh_end(ctx->files); ++k) { 552 | if (!kh_exist(ctx->files, k)) 553 | continue; 554 | f = kh_value(ctx->files, k); 555 | 556 | for (d = f->digests; d && (dtmp = d->next, 1); d = dtmp) { 557 | free(d->digest); 558 | free(d); 559 | } 560 | 561 | free(f->fname); 562 | free(f); 563 | } 564 | } 565 | 566 | kh_destroy(asignify_verify_hnode, ctx->files); 567 | free(ctx); 568 | } 569 | -------------------------------------------------------------------------------- /m4/ax_append_flag.m4: -------------------------------------------------------------------------------- 1 | # =========================================================================== 2 | # http://www.gnu.org/software/autoconf-archive/ax_append_flag.html 3 | # =========================================================================== 4 | # 5 | # SYNOPSIS 6 | # 7 | # AX_APPEND_FLAG(FLAG, [FLAGS-VARIABLE]) 8 | # 9 | # DESCRIPTION 10 | # 11 | # FLAG is appended to the FLAGS-VARIABLE shell variable, with a space 12 | # added in between. 13 | # 14 | # If FLAGS-VARIABLE is not specified, the current language's flags (e.g. 15 | # CFLAGS) is used. FLAGS-VARIABLE is not changed if it already contains 16 | # FLAG. If FLAGS-VARIABLE is unset in the shell, it is set to exactly 17 | # FLAG. 18 | # 19 | # NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. 20 | # 21 | # LICENSE 22 | # 23 | # Copyright (c) 2008 Guido U. Draheim 24 | # Copyright (c) 2011 Maarten Bosmans 25 | # 26 | # This program is free software: you can redistribute it and/or modify it 27 | # under the terms of the GNU General Public License as published by the 28 | # Free Software Foundation, either version 3 of the License, or (at your 29 | # option) any later version. 30 | # 31 | # This program is distributed in the hope that it will be useful, but 32 | # WITHOUT ANY WARRANTY; without even the implied warranty of 33 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 34 | # Public License for more details. 35 | # 36 | # You should have received a copy of the GNU General Public License along 37 | # with this program. If not, see . 38 | # 39 | # As a special exception, the respective Autoconf Macro's copyright owner 40 | # gives unlimited permission to copy, distribute and modify the configure 41 | # scripts that are the output of Autoconf when processing the Macro. You 42 | # need not follow the terms of the GNU General Public License when using 43 | # or distributing such scripts, even though portions of the text of the 44 | # Macro appear in them. The GNU General Public License (GPL) does govern 45 | # all other use of the material that constitutes the Autoconf Macro. 46 | # 47 | # This special exception to the GPL applies to versions of the Autoconf 48 | # Macro released by the Autoconf Archive. When you make and distribute a 49 | # modified version of the Autoconf Macro, you may extend this special 50 | # exception to the GPL to apply to your modified version as well. 51 | 52 | #serial 2 53 | 54 | AC_DEFUN([AX_APPEND_FLAG], 55 | [AC_PREREQ(2.59)dnl for _AC_LANG_PREFIX 56 | AS_VAR_PUSHDEF([FLAGS], [m4_default($2,_AC_LANG_PREFIX[FLAGS])])dnl 57 | AS_VAR_SET_IF(FLAGS, 58 | [case " AS_VAR_GET(FLAGS) " in 59 | *" $1 "*) 60 | AC_RUN_LOG([: FLAGS already contains $1]) 61 | ;; 62 | *) 63 | AC_RUN_LOG([: FLAGS="$FLAGS $1"]) 64 | AS_VAR_SET(FLAGS, ["AS_VAR_GET(FLAGS) $1"]) 65 | ;; 66 | esac], 67 | [AS_VAR_SET(FLAGS,["$1"])]) 68 | AS_VAR_POPDEF([FLAGS])dnl 69 | ])dnl AX_APPEND_FLAG 70 | -------------------------------------------------------------------------------- /m4/ax_cflags_warn_all.m4: -------------------------------------------------------------------------------- 1 | # =========================================================================== 2 | # http://www.gnu.org/software/autoconf-archive/ax_cflags_warn_all.html 3 | # =========================================================================== 4 | # 5 | # SYNOPSIS 6 | # 7 | # AX_CFLAGS_WARN_ALL [(shellvar [,default, [A/NA]])] 8 | # AX_CXXFLAGS_WARN_ALL [(shellvar [,default, [A/NA]])] 9 | # AX_FCFLAGS_WARN_ALL [(shellvar [,default, [A/NA]])] 10 | # 11 | # DESCRIPTION 12 | # 13 | # Try to find a compiler option that enables most reasonable warnings. 14 | # 15 | # For the GNU compiler it will be -Wall (and -ansi -pedantic) The result 16 | # is added to the shellvar being CFLAGS, CXXFLAGS, or FCFLAGS by default. 17 | # 18 | # Currently this macro knows about the GCC, Solaris, Digital Unix, AIX, 19 | # HP-UX, IRIX, NEC SX-5 (Super-UX 10), Cray J90 (Unicos 10.0.0.8), and 20 | # Intel compilers. For a given compiler, the Fortran flags are much more 21 | # experimental than their C equivalents. 22 | # 23 | # - $1 shell-variable-to-add-to : CFLAGS, CXXFLAGS, or FCFLAGS 24 | # - $2 add-value-if-not-found : nothing 25 | # - $3 action-if-found : add value to shellvariable 26 | # - $4 action-if-not-found : nothing 27 | # 28 | # NOTE: These macros depend on AX_APPEND_FLAG. 29 | # 30 | # LICENSE 31 | # 32 | # Copyright (c) 2008 Guido U. Draheim 33 | # Copyright (c) 2010 Rhys Ulerich 34 | # 35 | # This program is free software; you can redistribute it and/or modify it 36 | # under the terms of the GNU General Public License as published by the 37 | # Free Software Foundation; either version 3 of the License, or (at your 38 | # option) any later version. 39 | # 40 | # This program is distributed in the hope that it will be useful, but 41 | # WITHOUT ANY WARRANTY; without even the implied warranty of 42 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 43 | # Public License for more details. 44 | # 45 | # You should have received a copy of the GNU General Public License along 46 | # with this program. If not, see . 47 | # 48 | # As a special exception, the respective Autoconf Macro's copyright owner 49 | # gives unlimited permission to copy, distribute and modify the configure 50 | # scripts that are the output of Autoconf when processing the Macro. You 51 | # need not follow the terms of the GNU General Public License when using 52 | # or distributing such scripts, even though portions of the text of the 53 | # Macro appear in them. The GNU General Public License (GPL) does govern 54 | # all other use of the material that constitutes the Autoconf Macro. 55 | # 56 | # This special exception to the GPL applies to versions of the Autoconf 57 | # Macro released by the Autoconf Archive. When you make and distribute a 58 | # modified version of the Autoconf Macro, you may extend this special 59 | # exception to the GPL to apply to your modified version as well. 60 | 61 | #serial 15 62 | 63 | AC_DEFUN([AX_FLAGS_WARN_ALL],[dnl 64 | AS_VAR_PUSHDEF([FLAGS],[_AC_LANG_PREFIX[]FLAGS])dnl 65 | AS_VAR_PUSHDEF([VAR],[ac_cv_[]_AC_LANG_ABBREV[]flags_warn_all])dnl 66 | AC_CACHE_CHECK([m4_ifval($1,$1,FLAGS) for maximum warnings], 67 | VAR,[VAR="no, unknown" 68 | ac_save_[]FLAGS="$[]FLAGS" 69 | for ac_arg dnl 70 | in "-warn all % -warn all" dnl Intel 71 | "-pedantic % -Wall" dnl GCC 72 | "-xstrconst % -v" dnl Solaris C 73 | "-std1 % -verbose -w0 -warnprotos" dnl Digital Unix 74 | "-qlanglvl=ansi % -qsrcmsg -qinfo=all:noppt:noppc:noobs:nocnd" dnl AIX 75 | "-ansi -ansiE % -fullwarn" dnl IRIX 76 | "+ESlit % +w1" dnl HP-UX C 77 | "-Xc % -pvctl[,]fullmsg" dnl NEC SX-5 (Super-UX 10) 78 | "-h conform % -h msglevel 2" dnl Cray C (Unicos) 79 | # 80 | do FLAGS="$ac_save_[]FLAGS "`echo $ac_arg | sed -e 's,%%.*,,' -e 's,%,,'` 81 | AC_COMPILE_IFELSE([AC_LANG_PROGRAM], 82 | [VAR=`echo $ac_arg | sed -e 's,.*% *,,'` ; break]) 83 | done 84 | FLAGS="$ac_save_[]FLAGS" 85 | ]) 86 | AS_VAR_POPDEF([FLAGS])dnl 87 | AX_REQUIRE_DEFINED([AX_APPEND_FLAG]) 88 | case ".$VAR" in 89 | .ok|.ok,*) m4_ifvaln($3,$3) ;; 90 | .|.no|.no,*) m4_default($4,[m4_ifval($2,[AX_APPEND_FLAG([$2], [$1])])]) ;; 91 | *) m4_default($3,[AX_APPEND_FLAG([$VAR], [$1])]) ;; 92 | esac 93 | AS_VAR_POPDEF([VAR])dnl 94 | ])dnl AX_FLAGS_WARN_ALL 95 | dnl implementation tactics: 96 | dnl the for-argument contains a list of options. The first part of 97 | dnl these does only exist to detect the compiler - usually it is 98 | dnl a global option to enable -ansi or -extrawarnings. All other 99 | dnl compilers will fail about it. That was needed since a lot of 100 | dnl compilers will give false positives for some option-syntax 101 | dnl like -Woption or -Xoption as they think of it is a pass-through 102 | dnl to later compile stages or something. The "%" is used as a 103 | dnl delimiter. A non-option comment can be given after "%%" marks 104 | dnl which will be shown but not added to the respective C/CXXFLAGS. 105 | 106 | AC_DEFUN([AX_CFLAGS_WARN_ALL],[dnl 107 | AC_LANG_PUSH([C]) 108 | AX_FLAGS_WARN_ALL([$1], [$2], [$3], [$4]) 109 | AC_LANG_POP([C]) 110 | ]) 111 | 112 | AC_DEFUN([AX_CXXFLAGS_WARN_ALL],[dnl 113 | AC_LANG_PUSH([C++]) 114 | AX_FLAGS_WARN_ALL([$1], [$2], [$3], [$4]) 115 | AC_LANG_POP([C++]) 116 | ]) 117 | 118 | AC_DEFUN([AX_FCFLAGS_WARN_ALL],[dnl 119 | AC_LANG_PUSH([Fortran]) 120 | AX_FLAGS_WARN_ALL([$1], [$2], [$3], [$4]) 121 | AC_LANG_POP([Fortran]) 122 | ]) 123 | -------------------------------------------------------------------------------- /m4/ax_check_openssl.m4: -------------------------------------------------------------------------------- 1 | # =========================================================================== 2 | # http://www.gnu.org/software/autoconf-archive/ax_check_openssl.html 3 | # =========================================================================== 4 | # 5 | # SYNOPSIS 6 | # 7 | # AX_CHECK_OPENSSL([action-if-found[, action-if-not-found]]) 8 | # 9 | # DESCRIPTION 10 | # 11 | # Look for OpenSSL in a number of default spots, or in a user-selected 12 | # spot (via --with-openssl). Sets 13 | # 14 | # OPENSSL_INCLUDES to the include directives required 15 | # OPENSSL_LIBS to the -l directives required 16 | # OPENSSL_LDFLAGS to the -L or -R flags required 17 | # 18 | # and calls ACTION-IF-FOUND or ACTION-IF-NOT-FOUND appropriately 19 | # 20 | # This macro sets OPENSSL_INCLUDES such that source files should use the 21 | # openssl/ directory in include directives: 22 | # 23 | # #include 24 | # 25 | # LICENSE 26 | # 27 | # Copyright (c) 2009,2010 Zmanda Inc. 28 | # Copyright (c) 2009,2010 Dustin J. Mitchell 29 | # 30 | # Copying and distribution of this file, with or without modification, are 31 | # permitted in any medium without royalty provided the copyright notice 32 | # and this notice are preserved. This file is offered as-is, without any 33 | # warranty. 34 | 35 | #serial 8 36 | 37 | AU_ALIAS([CHECK_SSL], [AX_CHECK_OPENSSL]) 38 | AC_DEFUN([AX_CHECK_OPENSSL], [ 39 | found=false 40 | AC_ARG_WITH([openssl], 41 | [AS_HELP_STRING([--with-openssl=DIR], 42 | [root of the OpenSSL directory])], 43 | [ 44 | case "$withval" in 45 | "" | y | ye | yes | n | no) 46 | AC_MSG_ERROR([Invalid --with-openssl value]) 47 | ;; 48 | *) ssldirs="$withval" 49 | ;; 50 | esac 51 | ], [ 52 | # if pkg-config is installed and openssl has installed a .pc file, 53 | # then use that information and don't search ssldirs 54 | AC_PATH_PROG([PKG_CONFIG], [pkg-config]) 55 | if test x"$PKG_CONFIG" != x""; then 56 | OPENSSL_LDFLAGS=`$PKG_CONFIG openssl --libs-only-L 2>/dev/null` 57 | if test $? = 0; then 58 | OPENSSL_LIBS=`$PKG_CONFIG openssl --libs-only-l 2>/dev/null` 59 | OPENSSL_INCLUDES=`$PKG_CONFIG openssl --cflags-only-I 2>/dev/null` 60 | found=true 61 | fi 62 | fi 63 | 64 | # no such luck; use some default ssldirs 65 | if ! $found; then 66 | ssldirs="/usr/local/ssl /usr/lib/ssl /usr/ssl /usr/pkg /usr/local /usr" 67 | fi 68 | ] 69 | ) 70 | 71 | 72 | # note that we #include , so the OpenSSL headers have to be in 73 | # an 'openssl' subdirectory 74 | 75 | if ! $found; then 76 | OPENSSL_INCLUDES= 77 | for ssldir in $ssldirs; do 78 | AC_MSG_CHECKING([for openssl/ssl.h in $ssldir]) 79 | if test -f "$ssldir/include/openssl/ssl.h"; then 80 | OPENSSL_INCLUDES="-I$ssldir/include" 81 | OPENSSL_LDFLAGS="-L$ssldir/lib" 82 | OPENSSL_LIBS="-lssl -lcrypto" 83 | found=true 84 | AC_MSG_RESULT([yes]) 85 | break 86 | else 87 | AC_MSG_RESULT([no]) 88 | fi 89 | done 90 | 91 | # if the file wasn't found, well, go ahead and try the link anyway -- maybe 92 | # it will just work! 93 | fi 94 | 95 | # try the preprocessor and linker with our new flags, 96 | # being careful not to pollute the global LIBS, LDFLAGS, and CPPFLAGS 97 | 98 | AC_MSG_CHECKING([whether compiling and linking against OpenSSL works]) 99 | echo "Trying link with OPENSSL_LDFLAGS=$OPENSSL_LDFLAGS;" \ 100 | "OPENSSL_LIBS=$OPENSSL_LIBS; OPENSSL_INCLUDES=$OPENSSL_INCLUDES" >&AS_MESSAGE_LOG_FD 101 | 102 | save_LIBS="$LIBS" 103 | save_LDFLAGS="$LDFLAGS" 104 | save_CPPFLAGS="$CPPFLAGS" 105 | LDFLAGS="$LDFLAGS $OPENSSL_LDFLAGS" 106 | LIBS="$OPENSSL_LIBS $LIBS" 107 | CPPFLAGS="$OPENSSL_INCLUDES $CPPFLAGS" 108 | AC_LINK_IFELSE( 109 | [AC_LANG_PROGRAM([#include ], [SSL_new(NULL)])], 110 | [ 111 | AC_MSG_RESULT([yes]) 112 | $1 113 | ], [ 114 | AC_MSG_RESULT([no]) 115 | $2 116 | ]) 117 | CPPFLAGS="$save_CPPFLAGS" 118 | LDFLAGS="$save_LDFLAGS" 119 | LIBS="$save_LIBS" 120 | 121 | AC_SUBST([OPENSSL_INCLUDES]) 122 | AC_SUBST([OPENSSL_LIBS]) 123 | AC_SUBST([OPENSSL_LDFLAGS]) 124 | ]) 125 | -------------------------------------------------------------------------------- /m4/ax_require_defined.m4: -------------------------------------------------------------------------------- 1 | # =========================================================================== 2 | # http://www.gnu.org/software/autoconf-archive/ax_require_defined.html 3 | # =========================================================================== 4 | # 5 | # SYNOPSIS 6 | # 7 | # AX_REQUIRE_DEFINED(MACRO) 8 | # 9 | # DESCRIPTION 10 | # 11 | # AX_REQUIRE_DEFINED is a simple helper for making sure other macros have 12 | # been defined and thus are available for use. This avoids random issues 13 | # where a macro isn't expanded. Instead the configure script emits a 14 | # non-fatal: 15 | # 16 | # ./configure: line 1673: AX_CFLAGS_WARN_ALL: command not found 17 | # 18 | # It's like AC_REQUIRE except it doesn't expand the required macro. 19 | # 20 | # Here's an example: 21 | # 22 | # AX_REQUIRE_DEFINED([AX_CHECK_LINK_FLAG]) 23 | # 24 | # LICENSE 25 | # 26 | # Copyright (c) 2014 Mike Frysinger 27 | # 28 | # Copying and distribution of this file, with or without modification, are 29 | # permitted in any medium without royalty provided the copyright notice 30 | # and this notice are preserved. This file is offered as-is, without any 31 | # warranty. 32 | 33 | #serial 1 34 | 35 | AC_DEFUN([AX_REQUIRE_DEFINED], [dnl 36 | m4_ifndef([$1], [m4_fatal([macro ]$1[ is not defined; is a m4 file missing?])]) 37 | ])dnl AX_REQUIRE_DEFINED 38 | -------------------------------------------------------------------------------- /src/Makefile.am: -------------------------------------------------------------------------------- 1 | noinst_HEADERS= readpassphrase_compat.h \ 2 | cli.h 3 | 4 | bin_PROGRAMS=asignify 5 | 6 | asignify_SOURCES= asignify.c \ 7 | verify.c \ 8 | sign.c \ 9 | generate.c \ 10 | encrypt.c 11 | 12 | asignify_LDFLAGS = $(top_builddir)/libasignify/libasignify.la \ 13 | @OPENSSL_LDFLAGS@ \ 14 | @OPENSSL_LIBS@ \ 15 | @OS_LIBS@ 16 | asignify_CPPFLAGS = -I$(top_srcdir)/include \ 17 | @OS_CFLAGS@ \ 18 | @OPENSSL_INCLUDES@ 19 | -------------------------------------------------------------------------------- /src/asignify.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2015, Vsevolod Stakhov 2 | * All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are met: 6 | * * Redistributions of source code must retain the above copyright 7 | * notice, this list of conditions and the following disclaimer. 8 | * * Redistributions in binary form must reproduce the above copyright 9 | * notice, this list of conditions and the following disclaimer in the 10 | * documentation and/or other materials provided with the distribution. 11 | * 12 | * THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY 13 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 14 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 15 | * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY 16 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 17 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 18 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 19 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 20 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 21 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 22 | */ 23 | 24 | #ifdef HAVE_CONFIG_H 25 | #include "config.h" 26 | #endif 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | #include "asignify.h" 39 | #include "cli.h" 40 | 41 | int quiet = 0; 42 | 43 | static void 44 | usage(const char *error) 45 | { 46 | if (error) 47 | fprintf(stderr, "%s\n", error); 48 | 49 | fprintf(stderr, "usage:" 50 | "\tasignify [-q] %s\n" 51 | "\tasignify [-q] %s\n" 52 | "\tasignify [-q] %s\n" 53 | "\tasignify [-q] %s\n" 54 | "\tasignify [-q] %s\n", 55 | cli_verify_help(false), cli_check_help(false), 56 | cli_sign_help(false), cli_generate_help(false), 57 | cli_encrypt_help(false)); 58 | 59 | exit(EXIT_FAILURE); 60 | } 61 | 62 | static void 63 | version(void) 64 | { 65 | printf("Asignify %s\n", VERSION); 66 | exit(EXIT_SUCCESS); 67 | } 68 | 69 | static 70 | void help(bool failed, int argc, char **argv) 71 | { 72 | const char *ret = NULL; 73 | 74 | if (argc == 0) { 75 | usage(NULL); 76 | } 77 | else { 78 | if (strcasecmp(argv[0], "check") == 0) { 79 | ret = cli_check_help(true); 80 | } 81 | else if (strcasecmp(argv[0], "verify") == 0) { 82 | ret = cli_verify_help(true); 83 | } 84 | else if (strcasecmp(argv[0], "sign") == 0) { 85 | ret = cli_sign_help(true); 86 | } 87 | else if (strcasecmp(argv[0], "generate") == 0) { 88 | ret = cli_generate_help(true); 89 | } 90 | else if (strcasecmp(argv[0], "encrypt") == 0 || 91 | strcasecmp(argv[0], "decrypt") == 0) { 92 | ret = cli_encrypt_help(true); 93 | } 94 | else { 95 | usage("unknown command"); 96 | } 97 | 98 | if (ret != NULL) { 99 | if (failed) { 100 | fprintf(stderr, "%s", ret); 101 | exit(EXIT_FAILURE); 102 | } 103 | else { 104 | printf("%s", ret); 105 | exit(EXIT_SUCCESS); 106 | } 107 | } 108 | } 109 | 110 | } 111 | 112 | int 113 | main(int argc, char **argv) 114 | { 115 | int ch, ret = -1, i; 116 | static struct option long_options[] = { 117 | {"quiet", no_argument, 0, 'q' }, 118 | {"help", no_argument, 0, 'h' }, 119 | {"version", no_argument, 0, 'v' }, 120 | {0, 0, 0, 0 } 121 | }; 122 | char **our_argv; 123 | int our_argc; 124 | 125 | /* 126 | * Workaround to fix lack of brain of glibc authors: 127 | * getopt_long there tries to eat everything not stopping on arguments, 128 | * therefore, we need to stop that mess manually 129 | */ 130 | our_argv = malloc(argc * sizeof(char *)); 131 | our_argv[0] = argv[0]; 132 | our_argc = 1; 133 | 134 | for (i = 1; i < argc; i ++) { 135 | if (argv[i] != NULL && *argv[i] == '-') { 136 | our_argv[our_argc++] = argv[i]; 137 | } 138 | else { 139 | break; 140 | } 141 | } 142 | 143 | while ((ch = getopt_long(our_argc, our_argv, "qhv", long_options, NULL)) != -1) { 144 | switch (ch) { 145 | case 'q': 146 | quiet = 1; 147 | break; 148 | case 'v': 149 | version(); 150 | break; 151 | case 'h': 152 | default: 153 | usage(NULL); 154 | break; 155 | } 156 | } 157 | argc -= optind; 158 | argv += optind; 159 | 160 | /* Read command as the next argument */ 161 | if (argc == 0) { 162 | usage("must specify at least one command"); 163 | } 164 | 165 | /* reset getopt for the next call */ 166 | #ifdef __GLIBC__ 167 | optind = 0; 168 | #else 169 | optreset = 1; 170 | optind = 1; 171 | #endif 172 | 173 | if (strcasecmp(argv[0], "check") == 0) { 174 | ret = cli_check(argc, argv); 175 | } 176 | else if (strcasecmp(argv[0], "verify") == 0) { 177 | ret = cli_verify(argc, argv); 178 | } 179 | else if (strcasecmp(argv[0], "sign") == 0) { 180 | ret = cli_sign(argc, argv); 181 | } 182 | else if (strcasecmp(argv[0], "generate") == 0) { 183 | ret = cli_generate(argc, argv); 184 | } 185 | else if (strcasecmp(argv[0], "encrypt") == 0 || 186 | strcasecmp(argv[0], "decrypt") == 0) { 187 | ret = cli_encrypt(argc, argv); 188 | } 189 | else if (strcasecmp(argv[0], "help") == 0) { 190 | help(false, argc - 1, argv + 1); 191 | } else { 192 | usage("unknown command"); 193 | } 194 | 195 | if (ret == 0) { 196 | help(true, argc, argv); 197 | } 198 | else if (ret == -1) { 199 | exit(EXIT_FAILURE); 200 | } 201 | 202 | return (EXIT_SUCCESS); 203 | } 204 | -------------------------------------------------------------------------------- /src/cli.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2015, Vsevolod Stakhov 2 | * All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are met: 6 | * * Redistributions of source code must retain the above copyright 7 | * notice, this list of conditions and the following disclaimer. 8 | * * Redistributions in binary form must reproduce the above copyright 9 | * notice, this list of conditions and the following disclaimer in the 10 | * documentation and/or other materials provided with the distribution. 11 | * 12 | * THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY 13 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 14 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 15 | * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY 16 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 17 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 18 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 19 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 20 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 21 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 22 | */ 23 | #ifndef CLI_H_ 24 | #define CLI_H_ 25 | 26 | #ifdef HAVE_CONFIG_H 27 | #include "config.h" 28 | #endif 29 | 30 | extern int quiet; 31 | 32 | const char * cli_verify_help(bool full); 33 | int cli_verify(int argc, char **argv); 34 | 35 | const char * cli_check_help(bool full); 36 | int cli_check(int argc, char **argv); 37 | 38 | const char * cli_sign_help(bool full); 39 | int cli_sign(int argc, char **argv); 40 | 41 | const char * cli_generate_help(bool full); 42 | int cli_generate(int argc, char **argv); 43 | 44 | const char * cli_encrypt_help(bool full); 45 | int cli_encrypt(int argc, char **argv); 46 | 47 | #endif /* CLI_H_ */ 48 | -------------------------------------------------------------------------------- /src/encrypt.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2015, Vsevolod Stakhov 2 | * All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are met: 6 | * * Redistributions of source code must retain the above copyright 7 | * notice, this list of conditions and the following disclaimer. 8 | * * Redistributions in binary form must reproduce the above copyright 9 | * notice, this list of conditions and the following disclaimer in the 10 | * documentation and/or other materials provided with the distribution. 11 | * 12 | * THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY 13 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 14 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 15 | * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY 16 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 17 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 18 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 19 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 20 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 21 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 22 | */ 23 | 24 | #ifdef HAVE_CONFIG_H 25 | #include "config.h" 26 | #endif 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | #include "asignify.h" 39 | #include "cli.h" 40 | 41 | #ifdef HAVE_READPASSPHRASE_H 42 | #include 43 | #elif defined(HAVE_BSD_READPASSPHRASE_H) 44 | #include 45 | #else 46 | #include "readpassphrase_compat.h" 47 | #endif 48 | 49 | static int 50 | read_password(char *buf, size_t len, void *d) 51 | { 52 | char password[512]; 53 | int l; 54 | 55 | if (readpassphrase("Password:", password, sizeof(password), 0) != NULL) { 56 | l = strlen(password); 57 | memcpy(buf, password, l); 58 | explicit_memzero(password, sizeof(password)); 59 | 60 | return (l); 61 | } 62 | 63 | return (-1); 64 | } 65 | 66 | const char * 67 | cli_encrypt_help(bool full) 68 | { 69 | 70 | const char *fullmsg = "" 71 | "asignify [global_opts] encrypt/decrypt - encrypt or decrypt a file\n\n" 72 | "Usage: asignify encrypt [-d] \n" 73 | "\t-d Perform decryption\n" 74 | "\t-f Use less safe but faster encryption (chacha8)\n" 75 | "\tsecretkey Path to a secret key file encrypt and sign\n" 76 | "\tpubkey Path to a peer's public key (must not be related to secretkey)\n" 77 | "\tin Path to input file\n" 78 | "\tout Path to ouptut file (must be a regular file)\n"; 79 | 80 | if (!full) { 81 | return ("encrypt [-d] [-f] "); 82 | } 83 | 84 | return (fullmsg); 85 | } 86 | 87 | int 88 | cli_encrypt(int argc, char **argv) 89 | { 90 | asignify_encrypt_t *enc; 91 | const char *seckeyfile = NULL, *pubkeyfile = NULL, 92 | *infile = NULL, *outfile = NULL; 93 | int ch; 94 | bool decrypt = false; 95 | enum asignify_encrypt_type type = ASIGNIFY_ENCRYPT_SAFE; 96 | static struct option long_options[] = { 97 | {"fast", no_argument, 0, 'f' }, 98 | {"decrypt", required_argument, 0, 'd' }, 99 | {0, 0, 0, 0 } 100 | }; 101 | 102 | if (strcmp(argv[0], "decrypt") == 0) { 103 | decrypt = true; 104 | } 105 | 106 | while ((ch = getopt_long(argc, argv, "df", long_options, NULL)) != -1) { 107 | switch (ch) { 108 | case 'd': 109 | decrypt = true; 110 | break; 111 | case 'f': 112 | type = ASIGNIFY_ENCRYPT_FAST; 113 | break; 114 | default: 115 | return (0); 116 | break; 117 | } 118 | } 119 | argc -= optind; 120 | argv += optind; 121 | 122 | if (argc < 4) { 123 | return (0); 124 | } 125 | 126 | 127 | seckeyfile = argv[0]; 128 | pubkeyfile = argv[1]; 129 | infile = argv[2]; 130 | outfile = argv[3]; 131 | 132 | enc = asignify_encrypt_init(); 133 | 134 | if (!asignify_encrypt_load_privkey(enc, seckeyfile, read_password, NULL)) { 135 | fprintf(stderr, "cannot load private key %s: %s\n", seckeyfile, 136 | asignify_encrypt_get_error(enc)); 137 | asignify_encrypt_free(enc); 138 | return (-1); 139 | } 140 | 141 | if (!asignify_encrypt_load_pubkey(enc, pubkeyfile)) { 142 | fprintf(stderr, "cannot load public key %s: %s\n", pubkeyfile, 143 | asignify_encrypt_get_error(enc)); 144 | asignify_encrypt_free(enc); 145 | return (-1); 146 | } 147 | 148 | if (decrypt) { 149 | if (!asignify_encrypt_decrypt_file(enc, infile, outfile)) { 150 | fprintf(stderr, "cannot decrypt file %s: %s\n", infile, 151 | asignify_encrypt_get_error(enc)); 152 | unlink(outfile); 153 | asignify_encrypt_free(enc); 154 | return (-1); 155 | } 156 | } 157 | else { 158 | if (!asignify_encrypt_crypt_file(enc, 1, infile, outfile, type)) { 159 | fprintf(stderr, "cannot encrypt file %s: %s\n", infile, 160 | asignify_encrypt_get_error(enc)); 161 | unlink(outfile); 162 | asignify_encrypt_free(enc); 163 | return (-1); 164 | } 165 | } 166 | 167 | asignify_encrypt_free(enc); 168 | 169 | if (!quiet) { 170 | if (decrypt) { 171 | printf("Decrypted and verified %s using local secret key %s and remote " 172 | "public key %s, result saved in %s\n", 173 | infile, seckeyfile, pubkeyfile, outfile); 174 | } 175 | else { 176 | printf("Encrypted and signed %s using local secret key %s and remote " 177 | "public key %s, result saved in %s\n", 178 | infile, seckeyfile, pubkeyfile, outfile); 179 | } 180 | } 181 | 182 | return (1); 183 | } 184 | -------------------------------------------------------------------------------- /src/generate.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2015, Vsevolod Stakhov 2 | * All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are met: 6 | * * Redistributions of source code must retain the above copyright 7 | * notice, this list of conditions and the following disclaimer. 8 | * * Redistributions in binary form must reproduce the above copyright 9 | * notice, this list of conditions and the following disclaimer in the 10 | * documentation and/or other materials provided with the distribution. 11 | * 12 | * THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY 13 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 14 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 15 | * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY 16 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 17 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 18 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 19 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 20 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 21 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 22 | */ 23 | 24 | #ifdef HAVE_CONFIG_H 25 | #include "config.h" 26 | #endif 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | #include "asignify.h" 39 | #include "cli.h" 40 | 41 | #ifdef HAVE_READPASSPHRASE_H 42 | #include 43 | #elif defined(HAVE_BSD_READPASSPHRASE_H) 44 | #include 45 | #else 46 | #include "readpassphrase_compat.h" 47 | #endif 48 | 49 | static int 50 | read_password_verify(char *buf, size_t len, void *d) 51 | { 52 | char password[512], repeat[512]; 53 | int l1, l2; 54 | 55 | if (readpassphrase("Password: ", password, sizeof(password), 0) != NULL) { 56 | if (readpassphrase("Verify: ", repeat, sizeof(repeat), 0) != NULL) { 57 | l1 = strlen(password); 58 | l2 = strlen(repeat); 59 | if (l1 == l2 && l1 <= len) { 60 | if (memcmp(password, repeat, l1) == 0) { 61 | memcpy(buf, password, l1); 62 | explicit_memzero(password, sizeof(password)); 63 | explicit_memzero(repeat, sizeof(repeat)); 64 | 65 | return (l1); 66 | } 67 | } 68 | if (!quiet) { 69 | fprintf(stderr, "Password and verify mismatch\n"); 70 | } 71 | } 72 | } 73 | 74 | return (-1); 75 | } 76 | 77 | const char * 78 | cli_generate_help(bool full) 79 | { 80 | const char *fullmsg = "" 81 | "asignify [global_opts] generate - generates a keypair\n\n" 82 | "Usage: asignify generate [-n] [-p] [-r ] [-s ] []\n" 83 | "\t-n Do not encrypt secret key\n" 84 | "\t-p Regenerate the public key for the specified secretkey\n" 85 | "\t-r Specify number of PBKDF rounds for secret key\n" 86 | "\t-s Convert the specified ssh secret key to native secret key\n" 87 | "\tsecretkey Path to a secret key\n" 88 | "\tpubkey Path to a public key (default: .pub)\n"; 89 | 90 | if (!full) { 91 | return ("generate [-n] [-p] [-r ] [-s ] secretkey [publickey]"); 92 | } 93 | 94 | return (fullmsg); 95 | } 96 | 97 | int 98 | cli_generate(int argc, char **argv) 99 | { 100 | int rounds = PBKDF_MINROUNDS * 10, ch; 101 | char pubkeybuf[PATH_MAX]; 102 | const char *seckeyfile = NULL, *pubkeyfile = NULL, *sshkeyfile = NULL; 103 | bool pubkey_only = false; 104 | static struct option long_options[] = { 105 | {"no-password", no_argument, 0, 'n' }, 106 | {"pubkey-only", no_argument, 0, 'p' }, 107 | {"rounds", required_argument, 0, 'r' }, 108 | {"ssh", required_argument, 0, 's' }, 109 | {0, 0, 0, 0 } 110 | }; 111 | 112 | while ((ch = getopt_long(argc, argv, "npr:s:", long_options, NULL)) != -1) { 113 | switch (ch) { 114 | case 'n': 115 | rounds = 0; 116 | break; 117 | case 'p': 118 | pubkey_only = true; 119 | break; 120 | case 'r': 121 | rounds = strtoul(optarg, NULL, 10); 122 | break; 123 | case 's': 124 | sshkeyfile = optarg; 125 | break; 126 | default: 127 | return (0); 128 | break; 129 | } 130 | } 131 | argc -= optind; 132 | argv += optind; 133 | 134 | if (pubkey_only && sshkeyfile) { 135 | fprintf(stderr, "Invalid combination: -p and -s\n"); 136 | return (-1); 137 | } 138 | 139 | if (argc == 1) { 140 | /* We have only a secret key specified */ 141 | seckeyfile = argv[0]; 142 | snprintf(pubkeybuf, sizeof(pubkeybuf), "%s.pub", seckeyfile); 143 | pubkeyfile = pubkeybuf; 144 | } 145 | else if (argc == 2) { 146 | seckeyfile = argv[0]; 147 | pubkeyfile = argv[1]; 148 | } 149 | else { 150 | return (0); 151 | } 152 | 153 | if (pubkey_only) { 154 | if (!asignify_generate_pubkey(seckeyfile, pubkeyfile, 155 | read_password_verify, NULL)) { 156 | fprintf(stderr, "Cannot regenerate public key\n"); 157 | return (-1); 158 | } 159 | 160 | if (!quiet) { 161 | printf("Public key from %s has been saved in %s\n", 162 | seckeyfile, pubkeyfile); 163 | } 164 | } else if (sshkeyfile) { 165 | if (rounds > 0) { 166 | if (!asignify_privkey_from_ssh(sshkeyfile, seckeyfile, 1, rounds, 167 | read_password_verify, NULL)) { 168 | fprintf(stderr, "Cannot convert ssh key\n"); 169 | return (-1); 170 | } 171 | } 172 | else { 173 | if (!asignify_privkey_from_ssh(sshkeyfile, seckeyfile, 1, 0, 174 | NULL, NULL)) { 175 | fprintf(stderr, "Cannot convert ssh key\n"); 176 | return (-1); 177 | } 178 | } 179 | 180 | if (!quiet) { 181 | printf("%s secret key is saved in %s\n", 182 | rounds > 0 ? "Encrypted" : "Unencrypted", seckeyfile); 183 | } 184 | } 185 | else { 186 | if (rounds > 0) { 187 | if (!asignify_generate(seckeyfile, pubkeyfile, 1, rounds, 188 | read_password_verify, NULL)) { 189 | fprintf(stderr, "Cannot generate keypair\n"); 190 | return (-1); 191 | } 192 | } 193 | else { 194 | if (!asignify_generate(seckeyfile, pubkeyfile, 1, 0, 195 | NULL, NULL)) { 196 | fprintf(stderr, "Cannot generate keypair\n"); 197 | return (-1); 198 | } 199 | } 200 | 201 | if (!quiet) { 202 | printf("%s keypair is saved in %s (private) and %s (public)\n", 203 | rounds > 0 ? "Encrypted" : "Unencrypted", seckeyfile, pubkeyfile); 204 | } 205 | } 206 | 207 | return (1); 208 | } 209 | -------------------------------------------------------------------------------- /src/readpassphrase_compat.h: -------------------------------------------------------------------------------- 1 | /* $OpenBSD: readpassphrase.c,v 1.24 2013/11/24 23:51:29 deraadt Exp $ */ 2 | 3 | /* 4 | * Copyright (c) 2000-2002, 2007, 2010 5 | * Todd C. Miller 6 | * 7 | * Permission to use, copy, modify, and distribute this software for any 8 | * purpose with or without fee is hereby granted, provided that the above 9 | * copyright notice and this permission notice appear in all copies. 10 | * 11 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 | * 19 | * Sponsored in part by the Defense Advanced Research Projects 20 | * Agency (DARPA) and Air Force Research Laboratory, Air Force 21 | * Materiel Command, USAF, under agreement number F39502-99-1-0512. 22 | */ 23 | 24 | #ifndef READPASSPHRASE_COMPAT_H 25 | #define READPASSPHRASE_COMPAT_H 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | #define RPP_ECHO_OFF 0x00 /* Turn off echo (default). */ 38 | #define RPP_ECHO_ON 0x01 /* Leave echo on. */ 39 | #define RPP_REQUIRE_TTY 0x02 /* Fail if there is no tty. */ 40 | #define RPP_FORCELOWER 0x04 /* Force input to lower case. */ 41 | #define RPP_FORCEUPPER 0x08 /* Force input to upper case. */ 42 | #define RPP_SEVENBIT 0x10 /* Strip the high bit from input. */ 43 | #define RPP_STDIN 0x20 /* Read from stdin, not /dev/tty */ 44 | 45 | #ifndef _NSIG 46 | #define _NSIG 32 47 | #endif 48 | 49 | static volatile sig_atomic_t signo[_NSIG]; 50 | static void handler(int); 51 | 52 | static char * 53 | readpassphrase(const char *prompt, char *buf, size_t bufsiz, int flags) 54 | { 55 | ssize_t nr; 56 | int input, output, save_errno, i, need_restart; 57 | char ch, *p, *end; 58 | struct termios term, oterm; 59 | struct sigaction sa, savealrm, saveint, savehup, savequit, saveterm; 60 | struct sigaction savetstp, savettin, savettou, savepipe; 61 | 62 | /* I suppose we could alloc on demand in this case (XXX). */ 63 | if (bufsiz == 0) { 64 | errno = EINVAL; 65 | return(NULL); 66 | } 67 | 68 | restart: 69 | for (i = 0; i < _NSIG; i++) 70 | signo[i] = 0; 71 | nr = -1; 72 | save_errno = 0; 73 | need_restart = 0; 74 | /* 75 | * Read and write to /dev/tty if available. If not, read from 76 | * stdin and write to stderr unless a tty is required. 77 | */ 78 | if ((flags & RPP_STDIN) || 79 | (input = output = open("/dev/tty", O_RDWR)) == -1) { 80 | if (flags & RPP_REQUIRE_TTY) { 81 | errno = ENOTTY; 82 | return(NULL); 83 | } 84 | input = STDIN_FILENO; 85 | output = STDERR_FILENO; 86 | } 87 | 88 | /* 89 | * Turn off echo if possible. 90 | * If we are using a tty but are not the foreground pgrp this will 91 | * generate SIGTTOU, so do it *before* installing the signal handlers. 92 | */ 93 | if (input != STDIN_FILENO && tcgetattr(input, &oterm) == 0) { 94 | memcpy(&term, &oterm, sizeof(term)); 95 | if (!(flags & RPP_ECHO_ON)) 96 | term.c_lflag &= ~(ECHO | ECHONL); 97 | (void)tcsetattr(input, TCSAFLUSH, &term); 98 | } else { 99 | memset(&term, 0, sizeof(term)); 100 | term.c_lflag |= ECHO; 101 | memset(&oterm, 0, sizeof(oterm)); 102 | oterm.c_lflag |= ECHO; 103 | } 104 | 105 | /* 106 | * Catch signals that would otherwise cause the user to end 107 | * up with echo turned off in the shell. Don't worry about 108 | * things like SIGXCPU and SIGVTALRM for now. 109 | */ 110 | sigemptyset(&sa.sa_mask); 111 | sa.sa_flags = 0; /* don't restart system calls */ 112 | sa.sa_handler = handler; 113 | (void)sigaction(SIGALRM, &sa, &savealrm); 114 | (void)sigaction(SIGHUP, &sa, &savehup); 115 | (void)sigaction(SIGINT, &sa, &saveint); 116 | (void)sigaction(SIGPIPE, &sa, &savepipe); 117 | (void)sigaction(SIGQUIT, &sa, &savequit); 118 | (void)sigaction(SIGTERM, &sa, &saveterm); 119 | (void)sigaction(SIGTSTP, &sa, &savetstp); 120 | (void)sigaction(SIGTTIN, &sa, &savettin); 121 | (void)sigaction(SIGTTOU, &sa, &savettou); 122 | 123 | if (!(flags & RPP_STDIN)) 124 | (void)write(output, prompt, strlen(prompt)); 125 | end = buf + bufsiz - 1; 126 | p = buf; 127 | while ((nr = read(input, &ch, 1)) == 1 && ch != '\n' && ch != '\r') { 128 | if (p < end) { 129 | if ((flags & RPP_SEVENBIT)) 130 | ch &= 0x7f; 131 | if (isalpha((unsigned char)ch)) { 132 | if ((flags & RPP_FORCELOWER)) 133 | ch = (char)tolower((unsigned char)ch); 134 | if ((flags & RPP_FORCEUPPER)) 135 | ch = (char)toupper((unsigned char)ch); 136 | } 137 | *p++ = ch; 138 | } 139 | } 140 | *p = '\0'; 141 | save_errno = errno; 142 | if (!(term.c_lflag & ECHO)) 143 | (void)write(output, "\n", 1); 144 | 145 | /* Restore old terminal settings and signals. */ 146 | if (memcmp(&term, &oterm, sizeof(term)) != 0) { 147 | while (tcsetattr(input, TCSAFLUSH, &oterm) == -1 && 148 | errno == EINTR && !signo[SIGTTOU]) 149 | continue; 150 | } 151 | (void)sigaction(SIGALRM, &savealrm, NULL); 152 | (void)sigaction(SIGHUP, &savehup, NULL); 153 | (void)sigaction(SIGINT, &saveint, NULL); 154 | (void)sigaction(SIGQUIT, &savequit, NULL); 155 | (void)sigaction(SIGPIPE, &savepipe, NULL); 156 | (void)sigaction(SIGTERM, &saveterm, NULL); 157 | (void)sigaction(SIGTSTP, &savetstp, NULL); 158 | (void)sigaction(SIGTTIN, &savettin, NULL); 159 | (void)sigaction(SIGTTOU, &savettou, NULL); 160 | if (input != STDIN_FILENO) 161 | (void)close(input); 162 | 163 | /* 164 | * If we were interrupted by a signal, resend it to ourselves 165 | * now that we have restored the signal handlers. 166 | */ 167 | for (i = 0; i < _NSIG; i++) { 168 | if (signo[i]) { 169 | kill(getpid(), i); 170 | switch (i) { 171 | case SIGTSTP: 172 | case SIGTTIN: 173 | case SIGTTOU: 174 | need_restart = 1; 175 | } 176 | } 177 | } 178 | if (need_restart) 179 | goto restart; 180 | 181 | if (save_errno) 182 | errno = save_errno; 183 | return(nr == -1 ? NULL : buf); 184 | } 185 | 186 | static void handler(int s) 187 | { 188 | 189 | signo[s] = 1; 190 | } 191 | 192 | #endif 193 | -------------------------------------------------------------------------------- /src/sign.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2015, Vsevolod Stakhov 2 | * All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are met: 6 | * * Redistributions of source code must retain the above copyright 7 | * notice, this list of conditions and the following disclaimer. 8 | * * Redistributions in binary form must reproduce the above copyright 9 | * notice, this list of conditions and the following disclaimer in the 10 | * documentation and/or other materials provided with the distribution. 11 | * 12 | * THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY 13 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 14 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 15 | * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY 16 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 17 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 18 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 19 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 20 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 21 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 22 | */ 23 | 24 | #ifdef HAVE_CONFIG_H 25 | #include "config.h" 26 | #endif 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | #include "asignify.h" 39 | #include "cli.h" 40 | 41 | #ifdef HAVE_READPASSPHRASE_H 42 | #include 43 | #elif defined(HAVE_BSD_READPASSPHRASE_H) 44 | #include 45 | #else 46 | #include "readpassphrase_compat.h" 47 | #endif 48 | 49 | static int 50 | read_password(char *buf, size_t len, void *d) 51 | { 52 | char password[512]; 53 | int l; 54 | 55 | if (readpassphrase("Password:", password, sizeof(password), 0) != NULL) { 56 | l = strlen(password); 57 | memcpy(buf, password, l); 58 | explicit_memzero(password, sizeof(password)); 59 | 60 | return (l); 61 | } 62 | 63 | return (-1); 64 | } 65 | 66 | const char * 67 | cli_sign_help(bool full) 68 | { 69 | 70 | const char *fullmsg = "" 71 | "asignify [global_opts] sign - creates a signature\n\n" 72 | "Usage: asignify sign [-n] [-d ...] [file1 [file2...]]\n" 73 | "\t-n Do not record files sizes\n" 74 | "\t-d Write specific digest (sha256, sha512, blake2)\n" 75 | "\tsecretkey Path to a secret key file make a signature\n" 76 | "\tsignature Path to signature file to write\n" 77 | "\tfile A file that will be recorded in the signature digests\n"; 78 | 79 | if (!full) { 80 | return ("sign [-n] [-d ] secretkey signature [file1 [file2...]]"); 81 | } 82 | 83 | return (fullmsg); 84 | } 85 | 86 | struct digest_item { 87 | enum asignify_digest_type type; 88 | struct digest_item *next; 89 | }; 90 | 91 | int 92 | cli_sign(int argc, char **argv) 93 | { 94 | asignify_sign_t *sgn; 95 | const char *seckeyfile = NULL, *sigfile = NULL; 96 | int i; 97 | int ch; 98 | int ret = 1; 99 | int added = 0; 100 | bool no_size = false; 101 | /* XXX: we do not free this list on exit */ 102 | struct digest_item *dt_list = NULL, *dtit; 103 | enum asignify_digest_type dt; 104 | static struct option long_options[] = { 105 | {"no-size", no_argument, 0, 'n' }, 106 | {"digest", required_argument, 0, 'd' }, 107 | {0, 0, 0, 0 } 108 | }; 109 | 110 | while ((ch = getopt_long(argc, argv, "nd:", long_options, NULL)) != -1) { 111 | switch (ch) { 112 | case 'n': 113 | no_size = true; 114 | break; 115 | case 'd': 116 | dt = asignify_digest_from_str(optarg, strlen(optarg)); 117 | if (dt == ASIGNIFY_DIGEST_MAX) { 118 | fprintf(stderr, "bad digest type: %s\n", optarg); 119 | return (0); 120 | } 121 | dtit = malloc(sizeof(*dtit)); 122 | dtit->type = dt; 123 | dtit->next = dt_list; 124 | dt_list = dtit; 125 | break; 126 | default: 127 | return (0); 128 | break; 129 | } 130 | } 131 | argc -= optind; 132 | argv += optind; 133 | 134 | if (argc < 2) { 135 | return (0); 136 | } 137 | 138 | if (dt_list == NULL) { 139 | dt_list = malloc(sizeof(*dt_list)); 140 | dt_list->next = NULL; 141 | dt_list->type = ASIGNIFY_DIGEST_BLAKE2; 142 | } 143 | 144 | seckeyfile = argv[0]; 145 | sigfile = argv[1]; 146 | 147 | sgn = asignify_sign_init(); 148 | 149 | if (!asignify_sign_load_privkey(sgn, seckeyfile, read_password, NULL)) { 150 | fprintf(stderr, "cannot load private key %s: %s\n", seckeyfile, 151 | asignify_sign_get_error(sgn)); 152 | asignify_sign_free(sgn); 153 | return (-1); 154 | } 155 | 156 | for (i = 2; i < argc; i ++) { 157 | dtit = dt_list; 158 | while(dtit != NULL) { 159 | if (!asignify_sign_add_file(sgn, argv[i], dtit->type)) { 160 | fprintf(stderr, "cannot sign file %s: %s\n", argv[i], 161 | asignify_sign_get_error(sgn)); 162 | ret = -1; 163 | } 164 | else { 165 | if (!quiet) { 166 | printf("added %s digest of %s\n", 167 | asignify_digest_name(dtit->type), argv[i]); 168 | } 169 | added ++; 170 | } 171 | dtit = dtit->next; 172 | } 173 | if (!no_size) { 174 | if (!asignify_sign_add_file(sgn, argv[i], ASIGNIFY_DIGEST_SIZE)) { 175 | fprintf(stderr, "cannot calculated file size %s: %s\n", argv[i], 176 | asignify_sign_get_error(sgn)); 177 | ret = -1; 178 | } 179 | } 180 | } 181 | 182 | if (added == 0) { 183 | fprintf(stderr, "no digests has been added to the signature"); 184 | return (-1); 185 | } 186 | 187 | if (!asignify_sign_write_signature(sgn, sigfile)) { 188 | fprintf(stderr, "cannot write sign file %s: %s\n", sigfile, 189 | asignify_sign_get_error(sgn)); 190 | asignify_sign_free(sgn); 191 | return (-1); 192 | } 193 | 194 | asignify_sign_free(sgn); 195 | 196 | if (!quiet) { 197 | if (ret == 1) { 198 | printf("Digests file %s has been successfully signed\n", sigfile); 199 | } 200 | else { 201 | printf("Digests file %s has been signed but some files were not added due to errors\n", sigfile); 202 | } 203 | } 204 | 205 | return (ret); 206 | } 207 | -------------------------------------------------------------------------------- /src/verify.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2015, Vsevolod Stakhov 2 | * All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are met: 6 | * * Redistributions of source code must retain the above copyright 7 | * notice, this list of conditions and the following disclaimer. 8 | * * Redistributions in binary form must reproduce the above copyright 9 | * notice, this list of conditions and the following disclaimer in the 10 | * documentation and/or other materials provided with the distribution. 11 | * 12 | * THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY 13 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 14 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 15 | * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY 16 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 17 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 18 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 19 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 20 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 21 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 22 | */ 23 | 24 | #ifdef HAVE_CONFIG_H 25 | #include "config.h" 26 | #endif 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | #include "asignify.h" 39 | #include "cli.h" 40 | 41 | const char * 42 | cli_verify_help(bool full) 43 | { 44 | const char *fullmsg = "" 45 | "asignify [global_opts] verify - verifies signature\n\n" 46 | "Usage: asignify verify \n" 47 | "\tpubkey Path to a public key file to check signature against\n" 48 | "\tsignature Path to signature file to check\n"; 49 | 50 | if (!full) { 51 | return ("verify pubkey signature"); 52 | } 53 | 54 | return (fullmsg); 55 | } 56 | 57 | int 58 | cli_verify(int argc, char **argv) 59 | { 60 | asignify_verify_t *vrf; 61 | const char *pubkeyfile = NULL, *sigfile = NULL; 62 | 63 | if (argc != 3) { 64 | return (0); 65 | } 66 | 67 | /* Argv[0] == "verify" */ 68 | pubkeyfile = argv[1]; 69 | sigfile = argv[2]; 70 | 71 | vrf = asignify_verify_init(); 72 | if (!asignify_verify_load_pubkey(vrf, pubkeyfile)) { 73 | fprintf(stderr, "cannot load pubkey %s: %s\n", pubkeyfile, 74 | asignify_verify_get_error(vrf)); 75 | asignify_verify_free(vrf); 76 | return (-1); 77 | } 78 | 79 | if (!asignify_verify_load_signature(vrf, sigfile)) { 80 | fprintf(stderr, "cannot verify signature %s: %s\n", sigfile, 81 | asignify_verify_get_error(vrf)); 82 | asignify_verify_free(vrf); 83 | return (-1); 84 | } 85 | else if (!quiet) { 86 | printf("validated signature in %s\n", sigfile); 87 | } 88 | 89 | asignify_verify_free(vrf); 90 | 91 | return (1); 92 | } 93 | 94 | const char * 95 | cli_check_help(bool full) 96 | { 97 | const char *fullmsg = "" 98 | "asignify [global_opts] check - verifies signature and check external files validtiy\n\n" 99 | "Usage: asignify check ...\n" 100 | "\tpubkey Path to a public key file to check signature against\n" 101 | "\tsignature Path to signature file to check\n" 102 | "\tfile A file that is recorded in the signature digests\n"; 103 | 104 | if (!full) { 105 | return ("check pubkey signature file [file...]"); 106 | } 107 | 108 | return (fullmsg); 109 | } 110 | 111 | int 112 | cli_check(int argc, char **argv) 113 | { 114 | asignify_verify_t *vrf; 115 | const char *pubkeyfile = NULL, *sigfile = NULL; 116 | int i, ret = 1; 117 | 118 | if (argc < 4) { 119 | return (0); 120 | } 121 | 122 | pubkeyfile = argv[1]; 123 | sigfile = argv[2]; 124 | 125 | vrf = asignify_verify_init(); 126 | if (!asignify_verify_load_pubkey(vrf, pubkeyfile)) { 127 | fprintf(stderr, "cannot load pubkey %s: %s\n", pubkeyfile, 128 | asignify_verify_get_error(vrf)); 129 | asignify_verify_free(vrf); 130 | return (-1); 131 | } 132 | 133 | if (!asignify_verify_load_signature(vrf, sigfile)) { 134 | fprintf(stderr, "cannot verify signature %s: %s\n", sigfile, 135 | asignify_verify_get_error(vrf)); 136 | asignify_verify_free(vrf); 137 | return (-1); 138 | } 139 | else if (!quiet) { 140 | printf("validated signature in %s\n", sigfile); 141 | } 142 | 143 | for (i = 3; i < argc; i ++) { 144 | if (!asignify_verify_file(vrf, argv[i])) { 145 | fprintf(stderr, "verification failed for %s: %s\n", argv[i], 146 | asignify_verify_get_error(vrf)); 147 | ret = -1; 148 | } 149 | else if (!quiet) { 150 | printf("file %s has been verified\n", argv[i]); 151 | } 152 | } 153 | 154 | asignify_verify_free(vrf); 155 | 156 | return (ret); 157 | } 158 | --------------------------------------------------------------------------------