From 90566064a5c72d09c4d5164055cdb3eda2d696dc Mon Sep 17 00:00:00 2001 From: Duncan Overbruck Date: Sat, 25 Feb 2023 19:45:39 +0100 Subject: [PATCH] lib/crypto.c: add minisign ed25519 public-key cryptography api --- include/Makefile | 2 +- include/xbps/crypto.h | 346 +++++++++++++ lib/Makefile | 1 + lib/crypto.c | 777 +++++++++++++++++++++++++++++ lib/initend.c | 8 + tests/xbps/libxbps/Kyuafile | 1 + tests/xbps/libxbps/Makefile | 1 + tests/xbps/libxbps/crypto/Kyuafile | 5 + tests/xbps/libxbps/crypto/Makefile | 8 + tests/xbps/libxbps/crypto/main.c | 183 +++++++ 10 files changed, 1331 insertions(+), 1 deletion(-) create mode 100644 include/xbps/crypto.h create mode 100644 lib/crypto.c create mode 100644 tests/xbps/libxbps/crypto/Kyuafile create mode 100644 tests/xbps/libxbps/crypto/Makefile create mode 100644 tests/xbps/libxbps/crypto/main.c diff --git a/include/Makefile b/include/Makefile index 4b953bc5b..68a852e6d 100644 --- a/include/Makefile +++ b/include/Makefile @@ -10,7 +10,7 @@ all: install: install -d $(DESTDIR)$(INCLUDEDIR)/xbps install -m 644 $(INCS) $(DESTDIR)$(INCLUDEDIR) - for f in array bool data dictionary number object string; do \ + for f in array bool data dictionary number object string crypto; do \ install -m 644 xbps/xbps_$${f}.h $(DESTDIR)$(INCLUDEDIR)/xbps; \ done diff --git a/include/xbps/crypto.h b/include/xbps/crypto.h new file mode 100644 index 000000000..67441e1eb --- /dev/null +++ b/include/xbps/crypto.h @@ -0,0 +1,346 @@ +#ifndef XBPS_CRYPTO_H +#define XBPS_CRYPTO_H + +#include + +/** @addtogroup crypto */ +/**@{*/ + +#define B64_ENCODED_LEN(BIN_LEN) \ + sodium_base64_ENCODED_LEN(BIN_LEN, sodium_base64_VARIANT_ORIGINAL) + +/** + */ +struct xbps_hash_state { + crypto_generichash_state hs; +}; + +/** + * @struct xbps_hash + * @brief BLAKE2b hash. + */ +struct xbps_hash { + unsigned char mem[crypto_generichash_BYTES_MAX]; +}; + +/** + * @brief Initialize hash state. + */ +static inline int +xbps_hash_init(struct xbps_hash_state *state) +{ + return crypto_generichash_init(&state->hs, NULL, 0U, crypto_generichash_BYTES_MAX); +} + +/** + * @brief Update the hash state with new data. + */ +static inline int +xbps_hash_update(struct xbps_hash_state *state, + const unsigned char *in, unsigned long long inlen) +{ + return crypto_generichash_update(&state->hs, in, inlen); +} + +/** + * @brief Finalize the hash state and return the final hash. + */ +static inline int +xbps_hash_final(struct xbps_hash_state *state, struct xbps_hash *hash) +{ + return crypto_generichash_final(&state->hs, hash->mem, sizeof(hash->mem)); +} + +/** + * @brief Hash a file. + * + * @param[out] hash ::xbps_hash struct to store the hash. + * @param[in] path Path of the file to hash. + * + * @return 0 on success or a negative \c errno from \c open(3) or \c read(3). + */ +int xbps_hash_file(struct xbps_hash *hash, const char *path); + +/** + * @def SIGALG_HASHED + * @brief Ed25519 public-key signature of the BLAKE2b hash of the message. + */ +/** + * @def SIGALG + * @brief Ed25519 public-key signature of the message. + */ + +/** + * @def KEYNUMBYTES + * @brief Number of bytes used for keynum. + */ +#define KEYNUMBYTES 8 + +/** + * @def COMMENTMAXBYTES + * @brief Maximum bytes of untristed comments including trailing `\0`. + */ +#define COMMENTMAXBYTES 1024 + +/** + * @def TRUSTEDCOMMENTMAXBYTES + * @brief Maximum bytes of trusted comments including trailing `\0`. + */ +#define TRUSTEDCOMMENTMAXBYTES 8192 + +/** + * @def SIGBYTES + * @brief Number of bytes of the Ed25519 public-key signature. + */ +#define SIGBYTES crypto_sign_BYTES + +/** + * @def PUBLICKEYBYTES + * @brief Number of bytes of the Ed25519 public-key. + */ +#define PUBLICKEYBYTES crypto_sign_PUBLICKEYBYTES + +/** + * @def SECRETKEYBYTES + * @brief Number of bytes of the Ed25519 secret-key. + */ +#define SECRETKEYBYTES crypto_sign_SECRETKEYBYTES + +/** + * @def HASHBYTES + * @brief Number of bytes for the BLAKE2b hash. + */ +#define HASHBYTES crypto_generichash_BYTES + +/** + * @struct xbps_pubkey + * @brief minisign public-key. + * + * Algorithm id, keynum and Ed25519 public-key. + */ +struct xbps_pubkey { + /** + * @var sig_alg + * @brief Algorithm identifier. + */ + unsigned char sig_alg[2]; + /** + * @var keynum_pk + * @brief wtf + */ + struct { + /** + * @var keynum + * @brief key identifier + */ + unsigned char keynum[KEYNUMBYTES]; + /** + * @var pk + * @brief Ed25519 public-key + */ + unsigned char pk[PUBLICKEYBYTES]; + } keynum_pk; +}; + +#define PUBKEY_ENCODED_LEN (B64_ENCODED_LEN(sizeof(struct xbps_pubkey))) + +/** + * @brief Decode a base64 encoded minisign public-key. + * @param[out] pubkey Structure to decode the public-key to. + * @param[in] pubkey_s Encoded public-key. + * @returns \c 0 on success or a negative \c errno. + * @retval -ENOTSUP Public-key specifies a signature algorithm that is not supported. + * @retval -EINVAL Public-key is invalid. + */ +int xbps_pubkey_decode(struct xbps_pubkey *pubkey, const char *pubkey_s); + +/** + * @brief Encode a minising public-key using base64. + * @param[in] pubkey Public-key to encode. + * @param[out] pubkey_s Buffer to store the encoded public-key. + * @param[in] pubkey_s_len Size of the \p pubkey_s buffer. + * @returns \c 0 on success or a negative \c errno. + * @retval -ENOBUFS Encoded public-key would exceed the \p pubkey_s buffer. + */ +int xbps_pubkey_encode(const struct xbps_pubkey *pubkey, char *pubkey_s, size_t pubkey_s_len); + +/** + * @brief Read a minisign public-key file + * @param[out] pubkey Structure to store the public-key to. + * @param[in] fd File descriptor to read from + * @returns \c 0 on success or a negative \c errno from ::xbps_pubkey_decode, \c open(3) or \c read(3). + * @retval -ENOBUFS Comment or the encoded public-key exceed the maximum size. + */ +int xbps_pubkey_read(struct xbps_pubkey *pubkey, int fd); + +int xbps_pubkey_write(const struct xbps_pubkey *pubkey, const char *path); + +/** + * @struct xbps_seckey + * @brief minisign secret-key. + * + * Ed25519 secret-key, algorithm id and encryption data. + */ +struct xbps_seckey { + unsigned char sig_alg[2]; + unsigned char kdf_alg[2]; + unsigned char chk_alg[2]; + unsigned char kdf_salt[crypto_pwhash_scryptsalsa208sha256_SALTBYTES]; + unsigned char kdf_opslimit_le[8]; + unsigned char kdf_memlimit_le[8]; + struct { + unsigned char keynum[KEYNUMBYTES]; + unsigned char sk[SECRETKEYBYTES]; + /** + * @var chk + * @brief BLAKE2b hash of the secret-key. + * + * BLAKE2b hash of the secret-key used to check if the secret-key + * was successfully decrypted. + */ + unsigned char chk[HASHBYTES]; + } keynum_sk; +}; + +/** + * @brief Read and decrypt a secret-key. + * @param[out] seckey Structure to store the read secret-key in. + * @param[in] passphrase Optional passphrase to encrypt the secret-key. + * @param[in] path File descriptor to read from. + * @returns \c 0 on success or a negative \c errno. + * @retval -ENOBUFS Comment or encoded secret-key exceed the maximum size. + * @retval -ENOTSUP Secret-key used an algorithm or encryption that is not supported. + * @retval -EINVAL Secret-key is invalid. + * @retval -ERANGE Secret-key is encrypted but no passphrase was supplied or decryption failed. + * @retval -ENOMEM Secret-key decryption failed due to resource limits. + */ +int xbps_seckey_read(struct xbps_seckey *seckey, const char *passphrase, const char *path); + +/** + * @brief Write secret-key to file. + * @param[in] seckey Secret-key to write. + * @param[in] passphrase Optional passphrase to encrypt the secret-key with. + * @param[in] path Path to the file. + * @returns \c 0 on success or a negative \c errno from \c open(3) or \c write(3). + */ +int xbps_seckey_write(const struct xbps_seckey *seckey, const char *passphrase, const char *path); + +/** + * @struct xbps_sig + * @brief Ed25519 signature. + */ +struct xbps_sig { + /** + * @var sig_alg + * @brief Signature algorithm. + * + * The signature algorithm, currently only ::SIGALG_HASHED. + */ + unsigned char sig_alg[2]; + /** + * @var keynum + * @brief Key identifier. + * + * Cryptographically insecure. This should not be presented to users + * and is just used as a fastpath to check if a signature was signed + * by a different key. + */ + unsigned char keynum[KEYNUMBYTES]; + /** + * @var sig + * @brief Ed25519 public-key signature. + * + * Detached Ed25519 pubkey-key signature. + * + * The signature depends on the used ::xbps_sig.sig_alg: + * - ::SIGALG_HASHED: Signed BLAKE2b hash of the message. + * - ::SIGALG: Signature of the message itself, not supported. + */ + unsigned char sig[SIGBYTES]; +}; + +/** + * @struct xbps_minisig + * @brief minisig signature + */ +struct xbps_minisig { + /** + * @var comment + * @brief Untrusted comment in the .minisig file. + */ + char comment[COMMENTMAXBYTES]; + /** + * @var sig + * @brief Algorithm, keynum and signature of the signed data. + */ + struct xbps_sig sig; + /** + * @var trusted_comment + * @brief Trusted comment in the .minisig file. + */ + char trusted_comment[TRUSTEDCOMMENTMAXBYTES]; + /** + * @var global_sig + * @brief Signature of ::xbps_minisig.sig and ::xbps_minisig.trusted_comment. + * + * The global signature signs the signature and the trusted comment. + */ + unsigned char global_sig[SIGBYTES]; +}; + +/** + * @brief Read a minisig file. + * @param[out] minisig ::xbps_minisig struct to store the read data. + * @param[in] fd File descriptor to read from. + * @returns \c 0 on success or a negative \c errno from \c open(3), \c read(3). + * @retval -ENOBUFS Comments or the encoded signature exceed the maximum size. + */ +int xbps_minisig_read(struct xbps_minisig *minisig, int fd); + +/** + * @brief Write a minisig file. + * @param[in] minisig The ::xbps_minisig structure that is written + * @param[in] path Path to write to + * @returns \c 0 on success or a negative \c errno from \c open(3), \c write(3). + */ +int xbps_minisig_write(const struct xbps_minisig *minisig, const char *path); + +/** + * @brief Sign a minisig ::xbps_minisig. + * @param[out] minisig The ::xbps_minisig that is being signed and stores the signatures. + * @param[in] seckey Secret-key used to sign. + * @param[in] hash The hash of the message that is begin signed. + * @returns \c 0 on success or a negative \c errno. + * @retval -EINVAL Signing failed. + */ +int xbps_minisig_sign(struct xbps_minisig *minisig, const struct xbps_seckey *seckey, + const struct xbps_hash *hash); + +/** + * @brief Verify a minisig ::xbps_minisig. + * @param[in] minisig The ::xbps_minisig that is being verified. + * @param[in] pubkey Public-key used to verify the ::xbps_minisig. + * @param[in] hash The hash of the message that is being verified. + * @returns \c 0 on success or a negative \c errno. + * @retval -EINVAL \c keynum of \p pubkey does not match signature. + * @retval -ERANGE Signature verification failed. + */ +int xbps_minisig_verify(const struct xbps_minisig *minisig, const struct xbps_pubkey *pubkey, + const struct xbps_hash *hash); + +/** + * @brief Generate a new key pair. + * + * Generates a new public- and secret-key pair. + * + * @param[out] seckey ::xbps_seckey to store the generated secret-key. + * @param[out] pubkey ::xbps_pubkey to store the generated public-key. + * + * @return 0 on success or a negative \c errno. + * @retval -EINVAL Failure during keypair generation. + */ +int xbps_generate_keypair(struct xbps_seckey *seckey, struct xbps_pubkey *pubkey); + +/**@}*/ + +#endif diff --git a/lib/Makefile b/lib/Makefile index 0cf6ac84f..05a7990d4 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -52,6 +52,7 @@ OBJS += repo.o repo_sync.o OBJS += rpool.o cb_util.o proplib_wrapper.o OBJS += package_alternatives.o OBJS += conf.o log.o +OBJS += crypto.o OBJS += $(EXTOBJS) $(COMPAT_OBJS) # unnecessary unless pkgdb format changes # OBJS += pkgdb_conversion.o diff --git a/lib/crypto.c b/lib/crypto.c new file mode 100644 index 000000000..ee4960a1a --- /dev/null +++ b/lib/crypto.c @@ -0,0 +1,777 @@ +/*- + * Copyright (c) 2023 Duncan Overbruck . + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * Copyright (c) 2015-2018 + * Frank Denis + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#define _DEFAULT_SOURCE /* reallocarray */ +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include "xbps/crypto.h" +#include "xbps.h" + +#include "compat.h" + +#define PASSWORDMAXBYTES 1024 +#define SIGALG "Ed" +#define SIGALG_HASHED "ED" +#define KDFALG "Sc" +#define KDFNONE "\0\0" +#define CHKALG "B2" +#define COMMENT_PREFIX "untrusted comment: " +#define DEFAULT_COMMENT "signature from minisign secret key" +#define SECRETKEY_DEFAULT_COMMENT "minisign encrypted secret key" +#define TRUSTED_COMMENT_PREFIX "trusted comment: " +#define SIG_DEFAULT_CONFIG_DIR ".minisign" +#define SIG_DEFAULT_CONFIG_DIR_ENV_VAR "MINISIGN_CONFIG_DIR" +#define SIG_DEFAULT_PKFILE "minisign.pub" +#define SIG_DEFAULT_SKFILE "minisign.key" +#define SIG_SUFFIX ".minisig" +#define VERSION_STRING "minisign 0.11" + + +/** + * @file lib/crypto.c + * @brief Cryptography functinons + * @defgroup crypto Cryptography functions for public-key signatures + * + * Functions to sign and verify Ed25519 public-key signatues. + * + * The public-key signatures and secret keys are based on and comptaible with + * [minisign](https://jedisct1.github.io/minisign/) and use + * [libsodium](https://libsodium.org/). + */ + +static int +bin2base64(char *b64, size_t b64_len, const void *bin, size_t bin_len) +{ + assert(b64_len >= B64_ENCODED_LEN(bin_len)); + if (!sodium_bin2base64(b64, b64_len, bin, bin_len, sodium_base64_VARIANT_ORIGINAL)) + return -ENOBUFS; + return 0; +} + +static int +base642bin(void *bin, size_t bin_len, const char *b64, size_t b64_len) +{ + size_t decoded_len = 0; + if (sodium_base642bin(bin, bin_len, b64, b64_len, NULL, &decoded_len, + NULL, sodium_base64_VARIANT_ORIGINAL) != 0) + return -EINVAL; + if (decoded_len != bin_len) + return -EINVAL; + return 0; +} + +int +xbps_hash_file(struct xbps_hash *hash, const char *path) +{ + unsigned char buf[BUFSIZ]; + struct xbps_hash_state hs; + int fd; + int r; + + fd = open(path, O_RDONLY|O_CLOEXEC); + if (fd == -1) + return -errno; + + xbps_hash_init(&hs); + for (;;) { + ssize_t rd = read(fd, buf, sizeof(buf)); + if (rd < 0) { + if (errno == EINTR) + continue; + r = -errno; + close(fd); + return r; + } + if (rd == 0) + break; + xbps_hash_update(&hs, buf, rd); + } + xbps_hash_final(&hs, hash); + return 0; +} + +static int +pubkey_decode(struct xbps_pubkey *pubkey, const char *pubkey_s, size_t pubkey_s_len) +{ + size_t decoded_len; + int r; + + //, pubkey_s, sizeof(*pubkey), pubkey_s_len, &decoded_len + r = sodium_base642bin((unsigned char *)pubkey, sizeof(*pubkey), pubkey_s, pubkey_s_len, NULL, &decoded_len, NULL, sodium_base64_VARIANT_ORIGINAL); + if (r != 0 || decoded_len != sizeof(*pubkey)) { + return -EINVAL; + } + if (memcmp(pubkey->sig_alg, SIGALG, sizeof(pubkey->sig_alg)) != 0) { + xbps_dbg_printf("%s: unsupported public key signature algortihm\n", __FUNCTION__); + return -ENOTSUP; + } + return 0; +} + +int +xbps_pubkey_decode(struct xbps_pubkey *pubkey, const char *pubkey_s) +{ + return pubkey_decode(pubkey, pubkey_s, strlen(pubkey_s)); +} + +struct buf { + char *pos, *end; + char mem[BUFSIZ+1]; +}; + +static int +readline(int fd, struct buf *buf, char *dst, size_t dstsz, size_t *linelen) +{ + char *d = NULL; + size_t l = 0; + if (!buf->pos) { + buf->pos = buf->end = buf->mem; + *buf->pos = '\0'; + } + for (const char *scan = buf->pos; !(d = strpbrk(scan, "\r\n"));) { + ssize_t rd; + /* no newline in buffer, copy out data so we can read more at once */ + if (buf->end - buf->pos > 0) { + size_t n = buf->end - buf->pos; + if (l + n >= dstsz) + return -ENOBUFS; + memcpy(dst + l, buf->pos, n); + l += n; + scan = buf->pos = buf->end = buf->mem; + } + /* fill the buffer */ + rd = read(fd, buf->end, sizeof(buf->mem) - 1 - (buf->end - buf->pos)); + if (rd < 0) { + if (errno != EINTR) + return -errno; + continue; + } else if (rd == 0) { + break; + } + scan = buf->end; + buf->end += rd; + *buf->end = '\0'; + } + /* copy til EOF if there is no trailing newline */ + if (!d) + d = buf->end; + if (d > buf->pos) { + size_t n = d - buf->pos; + if (l + n >= dstsz) + return -ENOBUFS; + memcpy(dst + l, buf->pos, n); + l += n; + if (d == buf->end) { + buf->pos = buf->end; + } else { + buf->pos = d + 1 + (*d == '\r' && d[1] == '\n'); + } + } + dst[l] = '\0'; + if (linelen) + *linelen = l; + return 0; +} + +static int +writeflush(int fd, struct buf *buf) +{ + while (buf->pos < buf->end) { + ssize_t wr = write(fd, buf->pos, buf->end - buf->pos); + if (wr < 0) { + if (errno != EINTR) + return -errno; + continue; + } + buf->pos += wr; + } + buf->pos = buf->end = buf->mem; + return 0; +} + +static int +writebuf(int fd, struct buf *buf, const void *src, size_t srclen) +{ + if (!buf->pos) + buf->pos = buf->end = buf->mem; + while (srclen > 0) { + size_t avail = sizeof(buf->mem) - 1 - (buf->end - buf->pos); + size_t n; + if (avail == 0) { + int r = writeflush(fd, buf); + if (r < 0) + return r; + continue; + } + n = srclen < avail ? srclen : avail; + memcpy(buf->end, src, n); + srclen -= n; + src = (const char *)src + n; + buf->end += n; + *buf->end = '\0'; + } + return 0; +} + +static int +writestrs(int fd, struct buf *buf, ...) +{ + va_list ap; + int r; + + va_start(ap, buf); + for (;;) { + const char *s = va_arg(ap, const char *); + if (!s) + break; + r = writebuf(fd, buf, s, strlen(s)); + if (r < 0) + break; + } + va_end(ap); + return r; +} + +int +xbps_pubkey_read(struct xbps_pubkey *pubkey, int fd) +{ + char comment[COMMENTMAXBYTES]; + char pubkey_s[PUBKEY_ENCODED_LEN]; + struct buf buf = {0}; + size_t pubkey_s_len = 0; + int r; + + r = readline(fd, &buf, comment, sizeof(comment), NULL); + if (r < 0) { + xbps_dbg_printf("missing or invalid comment\n"); + return r; + } + r = readline(fd, &buf, pubkey_s, sizeof(pubkey_s), &pubkey_s_len); + if (r < 0) { + xbps_dbg_printf("missing or invalid base64 encoded public key\n"); + return r; + } + r = pubkey_decode(pubkey, pubkey_s, pubkey_s_len); + if (r < 0) { + xbps_dbg_printf("failed to decode base64 encoded public key: '%.*s'\n", + (int)pubkey_s_len, pubkey_s); + return r; + } + return 0; +} + +static uint64_t +le64_load(const unsigned char *p) +{ + return ((uint64_t) (p[0])) | ((uint64_t) (p[1]) << 8) | ((uint64_t) (p[2]) << 16) | + ((uint64_t) (p[3]) << 24) | ((uint64_t) (p[4]) << 32) | ((uint64_t) (p[5]) << 40) | + ((uint64_t) (p[6]) << 48) | ((uint64_t) (p[7]) << 56); +} + +int +xbps_pubkey_encode(const struct xbps_pubkey *pubkey, char *pubkey_s, size_t pubkey_s_len) +{ + return bin2base64(pubkey_s, pubkey_s_len, pubkey, sizeof(*pubkey)); +} + +int +xbps_pubkey_write(const struct xbps_pubkey *pubkey, const char *path) +{ + char comment[COMMENTMAXBYTES]; + char pubkey_s[PUBKEY_ENCODED_LEN]; + struct buf buf = {0}; + int fd; + int r; + + fd = open(path, O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC, 0644); + if (fd == -1) + return -errno; + + snprintf(comment, sizeof(comment), "minisign public key %" PRIX64, + le64_load(pubkey->keynum_pk.keynum)); + + xbps_pubkey_encode(pubkey, pubkey_s, sizeof(pubkey_s)); + + r = writestrs(fd, &buf, COMMENT_PREFIX, comment, "\n", pubkey_s, "\n", (char *)NULL); + if (r < 0) + goto err; + + r = writeflush(fd, &buf); + if (r < 0) + goto err; + + close(fd); + return 0; +err: + close(fd); + unlink(path); + return r; +} + +static void +xor_buf(unsigned char *dst, const unsigned char *src, size_t len) +{ + size_t i; + + for (i = (size_t) 0U; i < len; i++) { + dst[i] ^= src[i]; + } +} + +static void +le64_store(unsigned char *p, uint64_t x) +{ + p[0] = (unsigned char) x; + p[1] = (unsigned char) (x >> 8); + p[2] = (unsigned char) (x >> 16); + p[3] = (unsigned char) (x >> 24); + p[4] = (unsigned char) (x >> 32); + p[5] = (unsigned char) (x >> 40); + p[6] = (unsigned char) (x >> 48); + p[7] = (unsigned char) (x >> 56); +} + +static void +seckey_compute_chk(unsigned char chk[crypto_generichash_BYTES], const struct xbps_seckey *seckey) +{ + crypto_generichash_state hs; + + crypto_generichash_init(&hs, NULL, 0U, sizeof(seckey->keynum_sk.chk)); + crypto_generichash_update(&hs, seckey->sig_alg, sizeof(seckey->sig_alg)); + crypto_generichash_update(&hs, seckey->keynum_sk.keynum, + sizeof(seckey->keynum_sk.keynum)); + crypto_generichash_update(&hs, seckey->keynum_sk.sk, sizeof(seckey->keynum_sk.sk)); + crypto_generichash_final(&hs, chk, sizeof(seckey->keynum_sk.chk)); +} + +static int +encrypt_key(struct xbps_seckey *seckey, const char *passphrase) +{ + unsigned char stream[sizeof(seckey->keynum_sk)]; + unsigned long kdf_memlimit; + unsigned long kdf_opslimit; + + randombytes_buf(seckey->kdf_salt, sizeof(seckey->kdf_salt)); + kdf_opslimit = crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_SENSITIVE; + kdf_memlimit = crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_SENSITIVE; + while (crypto_pwhash_scryptsalsa208sha256(stream, sizeof(stream), + passphrase, strlen(passphrase), seckey->kdf_salt, kdf_opslimit, kdf_memlimit)) { + kdf_opslimit /= 2; + kdf_memlimit /= 2; + if (kdf_opslimit < crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN || + kdf_memlimit < crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN) + return -ENOMEM; + } + if (kdf_memlimit < crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_SENSITIVE) + xbps_warn_printf("due to limited memory the KDF used less memory than the default\n"); + memcpy(seckey->kdf_alg, KDFALG, sizeof(seckey->kdf_alg)); + le64_store(seckey->kdf_opslimit_le, kdf_opslimit); + le64_store(seckey->kdf_memlimit_le, kdf_memlimit); + seckey_compute_chk(seckey->keynum_sk.chk, seckey); + xor_buf((unsigned char *) (void *) &seckey->keynum_sk, stream, + sizeof(seckey->keynum_sk)); + sodium_memzero(&stream, sizeof(stream)); + return 0; +} + +int +xbps_seckey_write(const struct xbps_seckey *seckey, const char *passphrase, const char *path) +{ + char seckey_s[B64_ENCODED_LEN(sizeof(*seckey))]; + struct xbps_seckey seckey_enc; + struct buf buf = {0}; + int fd; + int r; + + if (passphrase) { + seckey_enc = *seckey; + r = encrypt_key(&seckey_enc, passphrase); + if (r < 0) { + sodium_memzero(&seckey_enc, sizeof(seckey_enc)); + return r; + } + seckey = &seckey_enc; + } + + fd = open(path, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0600); + if (fd == -1) + return -errno; + + fprintf(stderr, "size=%zu\n", sizeof(*seckey)); + bin2base64(seckey_s, sizeof(seckey_s), seckey, sizeof(*seckey)); + + r = writestrs(fd, &buf, COMMENT_PREFIX, SECRETKEY_DEFAULT_COMMENT, "\n", + seckey_s, "\n", (char *)NULL); + sodium_memzero(seckey_s, sizeof(seckey_s)); + if (r < 0) + goto err; + r = writeflush(fd, &buf); + if (r < 0) + goto err; + close(fd); + if (seckey == &seckey_enc) + sodium_memzero(&seckey_enc, sizeof(seckey_enc)); + return 0; +err: + if (seckey == &seckey_enc) + sodium_memzero(&seckey_enc, sizeof(seckey_enc)); + close(fd); + unlink(path); + return r; +} + +static int +decrypt_key(struct xbps_seckey *seckey, unsigned char chk[crypto_generichash_BYTES], + const char *passphrase) +{ + unsigned char stream[sizeof(seckey->keynum_sk)]; + if (crypto_pwhash_scryptsalsa208sha256(stream, sizeof(seckey->keynum_sk), + passphrase, strlen(passphrase), seckey->kdf_salt, + le64_load(seckey->kdf_opslimit_le), + le64_load(seckey->kdf_memlimit_le)) != 0) + return -ENOMEM; + xor_buf((unsigned char *) (void *) &seckey->keynum_sk, stream, + sizeof(seckey->keynum_sk)); + sodium_memzero(stream, sizeof(stream)); + seckey_compute_chk(chk, seckey); + if (memcmp(chk, seckey->keynum_sk.chk, sizeof(seckey->keynum_sk.chk)) != 0) + return -ERANGE; + return 0; +} + +static int +seckey_decode(struct xbps_seckey *seckey, const char *seckey_s, size_t seckey_s_len, + const char *passphrase) +{ + unsigned char chk[crypto_generichash_BYTES]; + size_t decoded_len; + int r; + + r = sodium_base642bin((unsigned char *)seckey, sizeof(*seckey), + seckey_s, seckey_s_len, NULL, &decoded_len, NULL, + sodium_base64_VARIANT_ORIGINAL); + if (r != 0 || decoded_len != sizeof(*seckey)) { + r = -EINVAL; + goto err; + } + if (memcmp(seckey->sig_alg, SIGALG, sizeof(seckey->sig_alg)) != 0 || + memcmp(seckey->chk_alg, CHKALG, sizeof(seckey->chk_alg)) != 0) { + r = -ENOTSUP; + goto err; + } + if (memcmp(seckey->kdf_alg, KDFALG, sizeof seckey->kdf_alg) == 0) { + if (!passphrase) { + r = -ERANGE; + goto err; + } + r = decrypt_key(seckey, chk, passphrase); + if (r < 0) + goto err; + } else if (memcmp(seckey->kdf_alg, KDFNONE, sizeof seckey->kdf_alg) == 0) { + /* unencrypted key */ + } else { + r = -ENOTSUP; + goto err; + } + return 0; +err: + sodium_memzero(seckey, sizeof(*seckey)); + return r; +} + +int +xbps_seckey_read(struct xbps_seckey *seckey, const char *passphrase, const char *path) +{ + char comment[COMMENTMAXBYTES]; + char seckey_s[B64_ENCODED_LEN(sizeof(*seckey))]; + struct buf buf = {0}; + size_t linelen = 0; + int fd; + int r; + + fd = open(path, O_RDONLY|O_CLOEXEC); + if (fd == -1) + return -errno; + + r = readline(fd, &buf, comment, sizeof(comment), &linelen); + if (r < 0) { + xbps_dbg_printf("%s: error reading comment: %s\n", + __FUNCTION__, strerror(-r)); + goto err; + } + r = readline(fd, &buf, seckey_s, sizeof(seckey_s), &linelen); + if (r < 0) { + xbps_dbg_printf("%s: error reading base64: %s\n", + __FUNCTION__, strerror(-r)); + goto err; + } + r = seckey_decode(seckey, seckey_s, linelen, passphrase); + if (r < 0) { + xbps_dbg_printf("%s: error decoding: %s\n", + __FUNCTION__, strerror(-r)); + goto err; + } + +err: + close(fd); + sodium_memzero(&buf, sizeof(buf)); + sodium_memzero(seckey_s, sizeof(seckey_s)); + return r; +} + +int +xbps_minisig_read(struct xbps_minisig *minisig, int fd) +{ + char sig_s[B64_ENCODED_LEN(sizeof(minisig->sig))]; + char global_sig_s[B64_ENCODED_LEN(sizeof(minisig->global_sig))]; + struct buf buf = {0}; + size_t sig_s_len = 0, global_sig_s_len = 0; + size_t comment_len = 0, trusted_comment_len = 0; + int r; + + r = readline(fd, &buf, minisig->comment, sizeof(minisig->comment), &comment_len); + if (r < 0) + return r; + if (strncmp(minisig->comment, COMMENT_PREFIX, sizeof(COMMENT_PREFIX) - 1) != 0) + return -EINVAL; + memmove(minisig->comment, + minisig->comment + sizeof(COMMENT_PREFIX) - 1, + comment_len - sizeof(COMMENT_PREFIX) + 2); + + r = readline(fd, &buf, sig_s, sizeof(sig_s), &sig_s_len); + if (r < 0) + return r; + r = base642bin(&minisig->sig, sizeof(minisig->sig), sig_s, sig_s_len); + if (r < 0) + return r; + if (memcmp(minisig->sig.sig_alg, SIGALG_HASHED, sizeof(minisig->sig.sig_alg)) != 0) + return -ENOTSUP; + + r = readline(fd, &buf, minisig->trusted_comment, sizeof(minisig->trusted_comment), + &trusted_comment_len); + if (r < 0) + return r; + if (strncmp(minisig->trusted_comment, TRUSTED_COMMENT_PREFIX, + sizeof(TRUSTED_COMMENT_PREFIX) - 1) != 0) + return -EINVAL; + memmove(minisig->trusted_comment, + minisig->trusted_comment + sizeof(TRUSTED_COMMENT_PREFIX) - 1, + trusted_comment_len - sizeof(TRUSTED_COMMENT_PREFIX) + 2); + + r = readline(fd, &buf, global_sig_s, sizeof(global_sig_s), &global_sig_s_len); + if (r < 0) + return r; + r = base642bin(minisig->global_sig, sizeof(minisig->global_sig), + global_sig_s, global_sig_s_len); + if (r < 0) + return r; + return 0; +} + +int +xbps_generate_keypair(struct xbps_seckey *seckey, struct xbps_pubkey *pubkey) +{ + randombytes_buf(seckey->keynum_sk.keynum, sizeof(seckey->keynum_sk.keynum)); + if (crypto_sign_keypair(pubkey->keynum_pk.pk, seckey->keynum_sk.sk) != 0) + return -EINVAL; + memcpy(seckey->sig_alg, SIGALG, sizeof(seckey->sig_alg)); + memcpy(seckey->kdf_alg, KDFNONE, sizeof(seckey->kdf_alg)); + memcpy(seckey->chk_alg, CHKALG, sizeof(seckey->chk_alg)); + seckey_compute_chk(seckey->keynum_sk.chk, seckey); + memcpy(pubkey->keynum_pk.keynum, seckey->keynum_sk.keynum, sizeof(pubkey->keynum_pk.keynum)); + memcpy(pubkey->sig_alg, SIGALG, sizeof(pubkey->sig_alg)); + return 0; +} + +static int +xbps_sig_sign(struct xbps_sig *sig, const struct xbps_seckey *seckey, const struct xbps_hash *hash) +{ + int r; + memcpy(sig->sig_alg, SIGALG_HASHED, sizeof(sig->sig_alg)); + memcpy(sig->keynum, seckey->keynum_sk.keynum, sizeof(sig->keynum)); + r = crypto_sign_detached(sig->sig, NULL, hash->mem, sizeof(hash->mem), seckey->keynum_sk.sk); + if (r != 0) + return -EINVAL; + return 0; +} + +int +xbps_minisig_sign(struct xbps_minisig *minisig, const struct xbps_seckey *seckey, + const struct xbps_hash *hash) +{ + unsigned char sig_and_trusted_comment[TRUSTEDCOMMENTMAXBYTES + sizeof(minisig->sig.sig)]; + size_t trusted_comment_len; + int r; + + r = xbps_sig_sign(&minisig->sig, seckey, hash); + if (r < 0) + return r; + + trusted_comment_len = strlen(minisig->trusted_comment); + memcpy(sig_and_trusted_comment, minisig->sig.sig, sizeof(minisig->sig.sig)); + memcpy(sig_and_trusted_comment + sizeof(minisig->sig.sig), minisig->trusted_comment, + trusted_comment_len); + if (crypto_sign_detached(minisig->global_sig, NULL, sig_and_trusted_comment, + sizeof(minisig->sig.sig) + trusted_comment_len, seckey->keynum_sk.sk) != 0) + return -EINVAL; + + return 0; +} + +static int +xbps_sig_verify(const struct xbps_sig *sig, const struct xbps_pubkey *pubkey, + const struct xbps_hash *hash) +{ + if (memcmp(sig->keynum, pubkey->keynum_pk.keynum, sizeof(sig->keynum)) != 0) + return -EINVAL; + if (crypto_sign_verify_detached(sig->sig, hash->mem, sizeof(hash->mem), + pubkey->keynum_pk.pk) != 0) { + xbps_dbg_printf("sig verification failed\n"); + return -ERANGE; + } + return 0; +} + +int +xbps_minisig_verify(const struct xbps_minisig *minisig, const struct xbps_pubkey *pubkey, + const struct xbps_hash *hash) +{ + unsigned char sig_and_trusted_comment[TRUSTEDCOMMENTMAXBYTES + sizeof(minisig->sig.sig)]; + size_t trusted_comment_len; + + int r; + r = xbps_sig_verify(&minisig->sig, pubkey, hash); + if (r < 0) + return r; + + trusted_comment_len = strlen(minisig->trusted_comment); + memcpy(sig_and_trusted_comment, minisig->sig.sig, sizeof(minisig->sig.sig)); + memcpy(sig_and_trusted_comment + sizeof(minisig->sig.sig), minisig->trusted_comment, + trusted_comment_len); + if (crypto_sign_verify_detached(minisig->global_sig, sig_and_trusted_comment, + sizeof(minisig->sig.sig) + trusted_comment_len, pubkey->keynum_pk.pk)) { + xbps_dbg_printf("global sig verification failed\n"); + return -ERANGE; + } + return 0; +} + +struct atomicfile { + char path[PATH_MAX]; +}; + +static int +atomicfile_open(struct atomicfile *a, const char *path) +{ + const char *fname; + int fd; + int l; + + fname = strrchr(path, '/'); + if (fname) { + size_t dirlen = fname - path; + if (dirlen >= PATH_MAX) { + errno = ENOBUFS; + return -1; + } + l = snprintf(a->path, sizeof(a->path), "%.*s/.%s.XXXXXXX", + (int)dirlen, path, fname+1); + } else { + l = snprintf(a->path, sizeof(a->path), ".%s.XXXXXXX", path); + } + if (l < 0 || (size_t)l >= sizeof(a->path)) { + errno = ENOBUFS; + return -1; + } + fd = mkstemp(a->path); + if (fd == -1) + return -1; + return fd; +} + +int +xbps_minisig_write(const struct xbps_minisig *minisig, const char *path) +{ + char sig_s[B64_ENCODED_LEN(sizeof(minisig->sig))]; + char global_sig_s[B64_ENCODED_LEN(sizeof(minisig->global_sig))]; + struct buf buf = {0}; + struct atomicfile tmpfile; + int fd; + int r; + + fd = atomicfile_open(&tmpfile, path); + if (fd == -1) + return -errno; + + bin2base64(sig_s, sizeof(sig_s), &minisig->sig, sizeof(minisig->sig)); + bin2base64(global_sig_s, sizeof(global_sig_s), + &minisig->global_sig, sizeof(minisig->global_sig)); + + r = writestrs(fd, &buf, COMMENT_PREFIX, minisig->comment, "\n", sig_s, + "\n", TRUSTED_COMMENT_PREFIX, minisig->trusted_comment, "\n", + global_sig_s, "\n", + (char *)NULL); + if (r < 0) + goto err; + r = writeflush(fd, &buf); + if (r < 0) + goto err; + if (rename(tmpfile.path, path) == -1) { + r = -errno; + goto err; + } + close(fd); + return 0; +err: + close(fd); + unlink(tmpfile.path); + return r; +} diff --git a/lib/initend.c b/lib/initend.c index 70ee26cb3..5d2cb3131 100644 --- a/lib/initend.c +++ b/lib/initend.c @@ -25,11 +25,14 @@ */ #include + #include #include #include #include +#include + #include "xbps_api_impl.h" /** @@ -54,6 +57,11 @@ xbps_init(struct xbps_handle *xhp) xbps_dbg_printf("%s\n", XBPS_RELVER); + if (sodium_init() != 0) { + xbps_dbg_printf("failed to initialize libsodium\n"); + return EINVAL; + } + /* Set rootdir */ if (xhp->rootdir[0] == '\0') { xhp->rootdir[0] = '/'; diff --git a/tests/xbps/libxbps/Kyuafile b/tests/xbps/libxbps/Kyuafile index 86a009d9e..edb9e7ad3 100644 --- a/tests/xbps/libxbps/Kyuafile +++ b/tests/xbps/libxbps/Kyuafile @@ -12,3 +12,4 @@ include('config/Kyuafile') include('find_pkg_orphans/Kyuafile') include('pkgdb/Kyuafile') include('shell/Kyuafile') +include('crypto/Kyuafile') diff --git a/tests/xbps/libxbps/Makefile b/tests/xbps/libxbps/Makefile index ca361bc65..9dad9916e 100644 --- a/tests/xbps/libxbps/Makefile +++ b/tests/xbps/libxbps/Makefile @@ -12,5 +12,6 @@ SUBDIRS += find_pkg_orphans SUBDIRS += pkgdb SUBDIRS += config SUBDIRS += shell +SUBDIRS += crypto include ../../../mk/subdir.mk diff --git a/tests/xbps/libxbps/crypto/Kyuafile b/tests/xbps/libxbps/crypto/Kyuafile new file mode 100644 index 000000000..f18aecc72 --- /dev/null +++ b/tests/xbps/libxbps/crypto/Kyuafile @@ -0,0 +1,5 @@ +syntax("kyuafile", 1) + +test_suite("libxbps") + +atf_test_program{name="crypto_test"} diff --git a/tests/xbps/libxbps/crypto/Makefile b/tests/xbps/libxbps/crypto/Makefile new file mode 100644 index 000000000..74e5f9a4c --- /dev/null +++ b/tests/xbps/libxbps/crypto/Makefile @@ -0,0 +1,8 @@ +TOPDIR = ../../../.. +-include $(TOPDIR)/config.mk + +TESTSSUBDIR = xbps/libxbps/util +TEST = crypto_test +EXTRA_FILES = Kyuafile + +include $(TOPDIR)/mk/test.mk diff --git a/tests/xbps/libxbps/crypto/main.c b/tests/xbps/libxbps/crypto/main.c new file mode 100644 index 000000000..846e5f304 --- /dev/null +++ b/tests/xbps/libxbps/crypto/main.c @@ -0,0 +1,183 @@ +/*- + * Copyright (c) 2023 Duncan Overbruck . + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *- + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +static const char *a_pub_content = + "untrusted comment: minisign public key 6161616161616161\n" + "YWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFh\n"; + +static const char *test_key_content UNUSED = + "untrusted comment: minisign encrypted secret key\n" + "RWQAAEIyAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAr5smWD4A8c3+JH5wEe+7C5dQbgSIS8lnvgSUiMGIYGbaZMh+wzTUux5FGmxu4PrfGd" + "NzVobtnluFTeELWHaqyU0dQhO5hzA7AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAA=\n"; + +static const char *test_pub_content = + "untrusted comment: minisign public key CDF1003E58269BAF\n" + "RWSvmyZYPgDxzR5FGmxu4PrfGdNzVobtnluFTeELWHaqyU0dQhO5hzA7\n"; + +static const char *test_pub_rn_content = + "untrusted comment: minisign public key CDF1003E58269BAF\r\n" + "RWSvmyZYPgDxzR5FGmxu4PrfGdNzVobtnluFTeELWHaqyU0dQhO5hzA7\r\n"; + +static const char *enobufs_pub_content = + "untrusted comment: minisign public key CDF1003E58269BAF\n" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n"; + + +ATF_TC(xbps_pubkey_decode); + +ATF_TC_HEAD(xbps_pubkey_decode, tc) +{ + atf_tc_set_md_var(tc, "descr", "Test xbps_pubkey_decode"); +} + +ATF_TC_BODY(xbps_pubkey_decode, tc) +{ + struct xbps_pubkey pubkey; + ATF_CHECK_EQ(xbps_pubkey_decode(&pubkey, "RWRfQ4v9r2BE5vDHBlJfZ1UL7byoLYM+jq22Sc34O+w0hW7NOtQZZ0nT"), 0); + ATF_CHECK_EQ(xbps_pubkey_decode(&pubkey, "RWRfQ4v9r2BE5vDHBlJfZ1UL7byoLYM+jq22Sc34O+w0hW7NOtQZZ0"), -EINVAL); + ATF_CHECK_EQ(xbps_pubkey_decode(&pubkey, "RWRfQ4v9r2BE5vDHBlJfZ1UL7byoLYM+jq22Sc34O+w0hW7NOtQZZ0nTAA"), -EINVAL); + /* algorith set to XX */ + ATF_CHECK_EQ(xbps_pubkey_decode(&pubkey, "WFhfQ4v9r2BE5vDHBlJfZ1UL7byoLYM+jq22Sc34O+w0hW7NOtQZZ0nT"), -ENOTSUP); +} + +ATF_TC(xbps_pubkey_encode); + +ATF_TC_HEAD(xbps_pubkey_encode, tc) +{ + atf_tc_set_md_var(tc, "descr", "Test xbps_pubkey_encode"); +} + +ATF_TC_BODY(xbps_pubkey_encode, tc) +{ + char pubkey_s[PUBKEY_ENCODED_LEN]; + struct xbps_pubkey pubkey; + int r; + memset(&pubkey, 'a', sizeof(pubkey)); + + r = xbps_pubkey_encode(&pubkey, pubkey_s, sizeof(pubkey_s)); + fprintf(stderr, "%d\n", r); + ATF_REQUIRE_EQ(r, 0); + ATF_CHECK_STREQ(pubkey_s, "YWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFh"); +} + +ATF_TC(xbps_pubkey_read); + +ATF_TC_HEAD(xbps_pubkey_read, tc) +{ + atf_tc_set_md_var(tc, "descr", "Test xbps_pubkey_read"); +} + +ATF_TC_BODY(xbps_pubkey_read, tc) +{ + struct xbps_pubkey pubkey; + int fd; + + memset(&pubkey, 'a', sizeof(pubkey)); + atf_utils_create_file("a.pub", "%s", a_pub_content); + + ATF_REQUIRE((fd = open("a.pub", O_RDONLY)) != -1); + ATF_REQUIRE_EQ(xbps_pubkey_read(&pubkey, fd), -ENOTSUP); + close(fd); + + atf_utils_create_file("test.pub", "%s", test_pub_content); + ATF_REQUIRE((fd = open("test.pub", O_RDONLY)) != -1); + ATF_REQUIRE_EQ(xbps_pubkey_read(&pubkey, fd), 0); + close(fd); + + atf_utils_create_file("test.pub", "%s", test_pub_rn_content); + ATF_REQUIRE((fd = open("test.pub", O_RDONLY)) != -1); + ATF_REQUIRE_EQ(xbps_pubkey_read(&pubkey, fd), 0); + close(fd); + + atf_utils_create_file("enobufs.pub", "%s", enobufs_pub_content); + ATF_REQUIRE((fd = open("enobufs.pub", O_RDONLY)) != -1); + ATF_REQUIRE_EQ(xbps_pubkey_read(&pubkey, fd), -ENOBUFS); + close(fd); +} + +ATF_TC(xbps_pubkey_write); + +ATF_TC_HEAD(xbps_pubkey_write, tc) +{ + atf_tc_set_md_var(tc, "descr", "Test xbps_pubkey_write"); +} + +ATF_TC_BODY(xbps_pubkey_write, tc) +{ + struct xbps_pubkey pubkey; + memset(&pubkey, 'a', sizeof(pubkey)); + ATF_REQUIRE(xbps_pubkey_write(&pubkey, "test.pub") == 0); + ATF_CHECK(atf_utils_compare_file("test.pub", a_pub_content)); + +} + +static const char *a_sec_content = + "untrusted comment: minisign encrypted secret key\n" + "YWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFh" + "YWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFh" + "YWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFh" + "YWFhYWE=\n"; + + +ATF_TC(xbps_seckey_write); + +ATF_TC_HEAD(xbps_seckey_write, tc) +{ + atf_tc_set_md_var(tc, "descr", "Test xbps_seckey_write"); +} + +ATF_TC_BODY(xbps_seckey_write, tc) +{ + struct xbps_seckey seckey; + memset(&seckey, 'a', sizeof(seckey)); + ATF_REQUIRE_EQ(xbps_seckey_write(&seckey, NULL, "test.key"), 0); + ATF_CHECK(atf_utils_compare_file("test.key", a_sec_content)); + ATF_REQUIRE_EQ(xbps_seckey_write(&seckey, NULL, "test.key"), -EEXIST); + +} + +ATF_TP_ADD_TCS(tp) +{ + xbps_debug_level = 1; + ATF_TP_ADD_TC(tp, xbps_pubkey_decode); + ATF_TP_ADD_TC(tp, xbps_pubkey_encode); + ATF_TP_ADD_TC(tp, xbps_pubkey_read); + ATF_TP_ADD_TC(tp, xbps_pubkey_write); + ATF_TP_ADD_TC(tp, xbps_seckey_write); + return atf_no_error(); +}