diff --git a/Makefile.am b/Makefile.am index 8f4d249..d13e662 100644 --- a/Makefile.am +++ b/Makefile.am @@ -5,7 +5,9 @@ lib_LTLIBRARIES = \ libbrandt.la libbrandt_la_SOURCES = \ - smc.c + crypto.c \ + util.c + libbrandt_la_LIBADD = \ -lpari diff --git a/crypto.c b/crypto.c new file mode 100644 index 0000000..399cd21 --- /dev/null +++ b/crypto.c @@ -0,0 +1,343 @@ +/* This file is part of libbrandt. + * Copyright (C) 2016 GNUnet e.V. + * + * libbrandt is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * libbrandt is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * libbrandt. If not, see . + */ + +/** + * @file crypto.c + * @brief Implementation of the crypto primitives. + */ + +#include + +#include "crypto.h" +#include "util.h" + +#define CURVE "Ed25519" + +/* --- RANDOM --- */ + +void +brandt_rand_poll () +{ + static unsigned char rand_amount = 255; + + if (!(rand_amount--)) + gcry_fast_random_poll (); +} + +/* --- HASHING --- */ + +/** + * Hash block of given size. + * + * @param block the data to #brandt_hash, length is given as a second argument + * @param size the length of the data to #brandt_hash in @a block + * @param ret pointer to where to write the hashcode + */ +void +brandt_hash (const void *block, size_t size, struct brandt_hash_code *ret) +{ + gcry_md_hash_buffer (GCRY_MD_SHA512, ret, block, size); +} + +/* --- MPI --- */ + +/** + * If target != size, move @a target bytes to the end of the size-sized + * buffer and zero out the first @a target - @a size bytes. + * + * @param buf original buffer + * @param size number of bytes in @a buf + * @param target target size of the buffer + */ +static void +adjust (void *buf, size_t size, size_t target) +{ + char *p = buf; + + if (size < target) + { + memmove (&p[target - size], buf, size); + memset (buf, 0, target - size); + } +} + +/** + * Output the given MPI value to the given buffer in + * network byte order. + * The MPI @a val may not be negative. + * + * @param buf where to output to + * @param size number of bytes in @a buf + * @param val value to write to @a buf + */ +void +brandt_mpi_print_unsigned (void *buf, size_t size, gcry_mpi_t val) +{ + size_t rsize; + gcry_error_t rc; + + if (gcry_mpi_get_flag (val, GCRYMPI_FLAG_OPAQUE)) + { + /* Store opaque MPIs left aligned into the buffer. */ + unsigned int nbits; + const void *p; + + p = gcry_mpi_get_opaque (val, &nbits); + brandt_assert (NULL != p); + rsize = (nbits + 7) / 8; + if (rsize > size) + rsize = size; + memcpy (buf, p, rsize); + if (rsize < size) + memset (((char *)buf) + rsize, 0, size - rsize); + } + else + { + /* Store regular MPIs as unsigned integers right aligned into the buffer. */ + rsize = size; + rc = gcry_mpi_print (GCRYMPI_FMT_USG, buf, rsize, &rsize, val); + brandt_assert_gpgerr (rc); + adjust (buf, rsize, size); + } +} + +/** + * Convert data buffer into MPI value. + * The buffer is interpreted as network + * byte order, unsigned integer. + * + * @param result where to store MPI value (allocated) + * @param data raw data (GCRYMPI_FMT_USG) + * @param size number of bytes in @a data + */ +void +brandt_mpi_scan_unsigned (gcry_mpi_t *result, const void *data, size_t size) +{ + gcry_error_t rc; + + rc = gcry_mpi_scan (result, GCRYMPI_FMT_USG, data, size, &size); + brandt_assert_gpgerr (rc); +} + +/* --- ECDHE --- */ + +/** + * Convert the given private key from the network format to the + * S-expression that can be used by libgcrypt. + * + * @param priv private key to decode + * @return NULL on error + */ +static gcry_sexp_t +decode_private_ecdhe_key (const struct brandt_dhe_skey *priv) +{ + gcry_sexp_t result; + gcry_error_t rc; + + rc = gcry_sexp_build (&result, NULL, + "(private-key(ecc(curve \"" CURVE "\")" + "(d %b)))", + (int)sizeof (priv->d), priv->d); + brandt_assert_gpgerr (rc); + return result; +} + +/** + * Extract values from an S-expression. + * + * @param array where to store the result(s) + * @param sexp S-expression to parse + * @param topname top-level name in the S-expression that is of interest + * @param elems names of the elements to extract + * @return 0 on success + */ +static int +key_from_sexp (gcry_mpi_t *array, gcry_sexp_t sexp, const char *topname, + const char *elems) +{ + gcry_sexp_t list; + gcry_sexp_t l2; + const char *s; + unsigned int i; + unsigned int idx; + + list = gcry_sexp_find_token (sexp, topname, 0); + if (!list) + return 1; + l2 = gcry_sexp_cadr (list); + gcry_sexp_release (list); + list = l2; + if (!list) + return 2; + idx = 0; + for (s = elems; *s; s++, idx++) + { + l2 = gcry_sexp_find_token (list, s, 1); + if (!l2) + { + for (i = 0; i < idx; i++) + { + gcry_free (array[i]); + array[i] = NULL; + } + gcry_sexp_release (list); + return 3; /* required parameter not found */ + } + array[idx] = gcry_sexp_nth_mpi (l2, 1, GCRYMPI_FMT_USG); + gcry_sexp_release (l2); + if (!array[idx]) + { + for (i = 0; i < idx; i++) + { + gcry_free (array[i]); + array[i] = NULL; + } + gcry_sexp_release (list); + return 4; /* required parameter is invalid */ + } + } + gcry_sexp_release (list); + return 0; +} + +/** + * Create a new private key. + * + * @param priv where to write the private key + */ +void +brandt_ecdhe_key_create (struct brandt_dhe_skey *priv) +{ + gcry_sexp_t priv_sexp; + gcry_sexp_t s_keyparam; + gcry_mpi_t d; + gcry_error_t rc; + + rc = gcry_sexp_build (&s_keyparam, NULL, "(genkey(ecc(curve \"" CURVE "\")" + "(flags)))") + brandt_assert_gpgerr (rc); + rc = gcry_pk_genkey (&priv_sexp, s_keyparam) + brandt_assert_gpgerr (rc); + gcry_sexp_release (s_keyparam); + rc = key_from_sexp (&d, priv_sexp, "private-key", "d") + brandt_assert_gpgerr (rc); + gcry_sexp_release (priv_sexp); + brandt_mpi_print_unsigned (priv->d, sizeof (priv->d), d); + gcry_mpi_release (d); +} + +/** + * Extract the public key for the given private key. + * + * @param priv the private key + * @param pub where to write the public key + */ +void +brandt_ecdhe_key_get_public (const struct brandt_dhe_skey *priv, + struct brandt_dhe_pkey *pub) +{ + gcry_sexp_t sexp; + gcry_ctx_t ctx; + gcry_mpi_t q; + gcry_error_t rc; + + sexp = decode_private_ecdhe_key (priv); + brandt_assert (NULL != sexp); + rc = gcry_mpi_ec_new (&ctx, sexp, NULL); + brandt_assert_gpgerr (rc); + gcry_sexp_release (sexp); + q = gcry_mpi_ec_get_mpi ("q@eddsa", ctx, 0); + brandt_assert (NULL != q); + brandt_mpi_print_unsigned (pub->q_y, sizeof (pub->q_y), q); + gcry_mpi_release (q); + gcry_ctx_release (ctx); +} + +/** + * Derive key material from a public and a private ECDHE key. + * + * @param priv private key to use for the ECDH (x) + * @param pub public key to use for the ECDH (yG) + * @param key_material where to write the key material (xyG) + * @return 0 on error, 1 on success + */ +int +brandt_ecdhe (const struct brandt_dhe_skey *priv, + const struct brandt_dhe_pkey *pub, + struct brandt_hash_code *key_material) +{ + gcry_error_t rc; + int rc2; + gcry_mpi_point_t result; + gcry_mpi_point_t q; + gcry_mpi_t d; + gcry_ctx_t ctx; + gcry_sexp_t pub_sexpr; + gcry_mpi_t result_x; + unsigned char xbuf[256 / 8]; + size_t rsize; + + /* first, extract the q = dP value from the public key */ + if (0 != gcry_sexp_build (&pub_sexpr, NULL, + "(public-key(ecc(curve " CURVE ")(q %b)))", + (int)sizeof (pub->q_y), pub->q_y)) + return 0; + rc = gcry_mpi_ec_new (&ctx, pub_sexpr, NULL); + brandt_assert_gpgerr (rc); + gcry_sexp_release (pub_sexpr); + q = gcry_mpi_ec_get_point ("q", ctx, 0); + + /* second, extract the d value from our private key */ + brandt_mpi_scan_unsigned (&d, priv->d, sizeof (priv->d)); + + /* then call the 'multiply' function, to compute the product */ + result = gcry_mpi_point_new (0); + gcry_mpi_ec_mul (result, d, q, ctx); + gcry_mpi_point_release (q); + gcry_mpi_release (d); + + /* finally, convert point to string for hashing */ + result_x = gcry_mpi_new (256); + rc = gcry_mpi_ec_get_affine (result_x, NULL, result, ctx); + brandt_assert (0 == rc); + gcry_mpi_point_release (result); + gcry_ctx_release (ctx); + + rsize = sizeof (xbuf); + rc2 = gcry_mpi_get_flag (result_x, GCRYMPI_FLAG_OPAQUE); + brandt_assert (0 == rc2); + /* result_x can be negative here, so we do not use 'brandt_mpi_print_unsigned' + * as that does not include the sign bit; x should be a 255-bit + * value, so with the sign it should fit snugly into the 256-bit + * xbuf */ + rc = gcry_mpi_print (GCRYMPI_FMT_STD, xbuf, rsize, &rsize, result_x); + brandt_assert_gpgerr (rc); + brandt_hash (xbuf, rsize, key_material); + gcry_mpi_release (result_x); + return 1; +} + +/** + * @ingroup crypto + * Clear memory that was used to store a private key. + * + * @param pk location of the key + */ +void +brandt_ecdhe_key_clear (struct brandt_dhe_skey *pk) +{ + memset (pk, 0, sizeof (struct brandt_dhe_skey)); +} diff --git a/crypto.h b/crypto.h new file mode 100644 index 0000000..c7ba816 --- /dev/null +++ b/crypto.h @@ -0,0 +1,71 @@ +/* This file is part of libbrandt. + * Copyright (C) 2016 GNUnet e.V. + * + * libbrandt is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * libbrandt is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * libbrandt. If not, see . + */ + +/** + * @file crypto.h + * @brief Interface of the crypto primitives. + */ + +#ifndef _BRANDT_CRYPTO_H +#define _BRANDT_CRYPTO_H + +#include +#include + +/* --- RANDOM --- */ + + +void brandt_rand_poll (); + + + +/* --- HASHING --- */ + +struct brandt_hash_code { + uint32_t bits[512 / 8 / sizeof (uint32_t)]; /* = 16 */ +}; + +void brandt_hash (const void *block, size_t size, struct brandt_hash_code *ret); + + + +/* --- MPI --- */ + +void brandt_mpi_print_unsigned (void *buf, size_t size, gcry_mpi_t val); +void brandt_mpi_scan_unsigned (gcry_mpi_t *result, const void *data, + size_t size); + + + +/* --- ECDHE --- */ + +struct brandt_dhe_skey { + unsigned char d[256 / 8]; +}; + +struct brandt_dhe_pkey { + unsigned char q_y[256 / 8]; +}; + +void brandt_ecdhe_key_create (struct brandt_dhe_skey *priv); +void brandt_ecdhe_key_get_public (const struct brandt_dhe_skey *priv, + struct brandt_dhe_pkey *pub); +int brandt_ecdhe (const struct brandt_dhe_skey *priv, + const struct brandt_dhe_pkey *pub, + struct brandt_hash_code *key_material); +void brandt_ecdhe_key_clear (struct brandt_dhe_skey *priv); + +#endif /* ifndef _BRANDT_CRYPTO_H */