/* * ecdsa.c * ------- * Elliptic Curve Digital Signature Algorithm for NIST prime curves. * * At some point we may want to refactor this code to separate * functionality that applies to all elliptic curve cryptography into * a separate module from functions specific to ECDSA over the NIST * prime curves, but it's simplest to keep this all in one place * initially. * * Much of the code in this module is based, at least loosely, on Tom * St Denis's libtomcrypt code. Algorithms for point addition and * point doubling courtesy of the hyperelliptic.org formula database. * * Authors: Rob Austein * Copyright (c) 2015, NORDUnet A/S * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * - 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. * * - Neither the name of the NORDUnet nor the names of its contributors may * be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT * HOLDER OR CONTRIBUTORS 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. */ /* * We use "Tom's Fast Math" library for our bignum implementation. * This particular implementation has a couple of nice features: * * - The code is relatively readable, thus reviewable. * * - The bignum representation doesn't use dynamic memory, which * simplifies things for us. * * The price tag for not using dynamic memory is that libtfm has to be * configured to know about the largest bignum one wants it to be able * to support at compile time. This should not be a serious problem. * * We use a lot of one-element arrays (fp_int[1] instead of plain * fp_int) to avoid having to prefix every use of an fp_int with "&". * Perhaps we should encapsulate this idiom in a typedef. * * Unfortunately, libtfm is bad about const-ification, but we want to * hide that from our users, so our public API uses const as * appropriate and we use inline functions to remove const constraints * in a relatively type-safe manner before calling libtom. */ #include #include "hal.h" #include "hal_internal.h" #include #include "asn1_internal.h" /* * Whether we're using static test vectors instead of the random * number generator. Do NOT enable this in production (doh). */ #ifndef HAL_ECDSA_DEBUG_ONLY_STATIC_TEST_VECTOR_RANDOM #define HAL_ECDSA_DEBUG_ONLY_STATIC_TEST_VECTOR_RANDOM 0 #endif #if defined(RPC_CLIENT) && RPC_CLIENT != RPC_CLIENT_LOCAL #define hal_get_random(core, buffer, length) hal_rpc_get_random(buffer, length) #endif /* * Whether to use the Verilog point multipliers. */ #ifndef HAL_ECDSA_VERILOG_ECDSA256_MULTIPLIER #define HAL_ECDSA_VERILOG_ECDSA256_MULTIPLIER 1 #endif #ifndef HAL_ECDSA_VERILOG_ECDSA384_MULTIPLIER #define HAL_ECDSA_VERILOG_ECDSA384_MULTIPLIER 1 #endif /* * Whether we want debug output. */ static int debug = 0; void hal_ecdsa_set_debug(const int onoff) { debug = onoff; } /* * ECDSA curve descriptor. We only deal with named curves; at the * moment, we only deal with NIST prime curves where the elliptic * curve's "a" parameter is always -3 and its "h" value (order of * elliptic curve group divided by order of base point) is always 1. * * Since the Montgomery parameters we need for the point arithmetic * depend only on the underlying field prime, we precompute them when * we load the curve and store them in the field descriptor, even * though they aren't really curve parameters per se. * * For similar reasons, we also include the ASN.1 OBJECT IDENTIFIERs * used to name these curves. */ typedef struct { fp_int q[1]; /* Modulus of underlying prime field */ fp_int b[1]; /* Curve's "b" parameter */ fp_int Gx[1]; /* x component of base point G */ fp_int Gy[1]; /* y component of base point G */ fp_int n[1]; /* Order of base point G */ fp_int mu[1]; /* Montgomery normalization factor */ fp_digit rho; /* Montgomery reduction value */ const uint8_t *oid; /* OBJECT IDENTIFIER */ size_t oid_len; /* Length of OBJECT IDENTIFIER */ hal_curve_name_t curve; /* Curve name */ } ecdsa_curve_t; /* * ECDSA key implementation. This structure type is private to this * module, anything else that needs to touch one of these just gets a * typed opaque pointer. We do, however, export the size, so that we * can make memory allocation the caller's problem. * * EC points are stored in Jacobian format such that (x, y, z) => * (x/z**2, y/z**3, 1) when interpretted as affine coordinates. * * There are really three different representations in use here: * * 1) Plain affine representation (z == 1); * 2) Montgomery form affine representation (z == curve->mu); and * 3) Montgomery form Jacobian representation (whatever). * * Only form (1) is ever visible outside this module. We perform * explicit conversions from form (1) to form (2) and from forms (2,3) * to form (1). Form (3) only occurs as the result of compuation. * * In theory, we could shave some microscopic amount of time off of * signature verification by supporting an explicit conversion from * form (3) to form (2), but it's not worth the additional complexity. */ typedef struct { fp_int x[1], y[1], z[1]; } ec_point_t; struct hal_ecdsa_key { hal_key_type_t type; /* Public or private */ hal_curve_name_t curve; /* Curve descriptor */ ec_point_t Q[1]; /* Public key */ fp_int d[1]; /* Private key */ }; const size_t hal_ecdsa_key_t_size = sizeof(struct hal_ecdsa_key); /* * Initializers. We want to be able to initialize automatic fp_int * and ec_point_t variables to a sane value (less error prone), but * picky compilers whine about the number of curly braces required. * So we define macros which isolate that madness in one place, and * use those macros everywhere. */ #define INIT_FP_INT {{{0}}} #define INIT_EC_POINT_T {{INIT_FP_INT}} /* * Error handling. */ #define lose(_code_) do { err = _code_; goto fail; } while (0) /* * We can't (usefully) initialize fp_int variables to non-zero values * at compile time, so instead we load all the curve parameters the * first time anything asks for any of them. */ static const ecdsa_curve_t * get_curve(const hal_curve_name_t curve) { static ecdsa_curve_t curve_p256, curve_p384, curve_p521; static int initialized = 0; if (!initialized) { #include "ecdsa_curves.h" fp_read_unsigned_bin(curve_p256.q, unconst_uint8_t(p256_q), sizeof(p256_q)); fp_read_unsigned_bin(curve_p256.b, unconst_uint8_t(p256_b), sizeof(p256_b)); fp_read_unsigned_bin(curve_p256.Gx, unconst_uint8_t(p256_Gx), sizeof(p256_Gx)); fp_read_unsigned_bin(curve_p256.Gy, unconst_uint8_t(p256_Gy), sizeof(p256_Gy)); fp_read_unsigned_bin(curve_p256.n, unconst_uint8_t(p256_n), sizeof(p256_n)); if (fp_montgomery_setup(curve_p256.q, &curve_p256.rho) != FP_OKAY) return NULL; fp_zero(curve_p256.mu); fp_montgomery_calc_normalization(curve_p256.mu, curve_p256.q); curve_p256.oid = p256_oid; curve_p256.oid_len = sizeof(p256_oid); curve_p256.curve = HAL_CURVE_P256; fp_read_unsigned_bin(curve_p384.q, unconst_uint8_t(p384_q), sizeof(p384_q)); fp_read_unsigned_bin(curve_p384.b, unconst_uint8_t(p384_b), sizeof(p384_b)); fp_read_unsigned_bin(curve_p384.Gx, unconst_uint8_t(p384_Gx), sizeof(p384_Gx)); fp_read_unsigned_bin(curve_p384.Gy, unconst_uint8_t(p384_Gy), sizeof(p384_Gy)); fp_read_unsigned_bin(curve_p384.n, unconst_uint8_t(p384_n), sizeof(p384_n)); if (fp_montgomery_setup(curve_p384.q, &curve_p384.rho) != FP_OKAY) return NULL; fp_zero(curve_p384.mu); fp_montgomery_calc_normalization(curve_p384.mu, curve_p384.q); curve_p384.oid = p384_oid; curve_p384.oid_len = sizeof(p384_oid); curve_p384.curve = HAL_CURVE_P384; fp_read_unsigned_bin(curve_p521.q, unconst_uint8_t(p521_q), sizeof(p521_q)); fp_read_unsigned_bin(curve_p521.b, unconst_uint8_t(p521_b), sizeof(p521_b)); fp_read_unsigned_bin(curve_p521.Gx, unconst_uint8_t(p521_Gx), sizeof(p521_Gx)); fp_read_unsigned_bin(curve_p521.Gy, unconst_uint8_t(p521_Gy), sizeof(p521_Gy)); fp_read_unsigned_bin(curve_p521.n, unconst_uint8_t(p521_n), sizeof(p521_n)); if (fp_montgomery_setup(curve_p521.q, &curve_p521.rho) != FP_OKAY) return NULL; fp_zero(curve_p521.mu); fp_montgomery_calc_normalization(curve_p521.mu, curve_p521.q); curve_p521.oid = p521_oid; curve_p521.oid_len = sizeof(p521_oid); curve_p521.curve = HAL_CURVE_P521; initialized = 1; } switch (curve) { case HAL_CURVE_P256: return &curve_p256; case HAL_CURVE_P384: return &curve_p384; case HAL_CURVE_P521: return &curve_p521; default: return NULL; } } hal_error_t hal_ecdsa_oid_to_curve(hal_curve_name_t *curve_name, const uint8_t * const oid, const size_t oid_len) { if (curve_name == NULL || oid == NULL) return HAL_ERROR_BAD_ARGUMENTS; *curve_name = HAL_CURVE_NONE; const ecdsa_curve_t *curve; while ((curve = get_curve(++*curve_name)) != NULL) if (oid_len == curve->oid_len && memcmp(oid, curve->oid, oid_len) == 0) return HAL_OK; return HAL_ERROR_UNSUPPORTED_KEY; } /* * Finite field operations (hence "ff_"). These are basically just * the usual bignum operations, constrained by the field modulus. * * All of these are operations in the field underlying the specified * curve, and assume that operands are already in Montgomery form. * * The ff_add() and ff_sub() are written a bit oddly, in an attempt to * make them run in constant time. An optimizing compiler may be * clever enough to defeat this. In the long run, we probably want to * perform these field operations in Verilog anyway. * * We might be able to squeeze a bit more speed out of the point * arithmetic by making using fp_mul_2d() when multiplying by a power * of two. Skipping for now as a premature optimization, but if we do * need this, it'd probably be simplest to add a ff_dbl() function * which handles overflow in the same way that ff_add() does. */ static inline void ff_add(const ecdsa_curve_t * const curve, const fp_int * const a, const fp_int * const b, fp_int *c) { fp_int t[2][1] = {INIT_FP_INT}; fp_add(unconst_fp_int(a), unconst_fp_int(b), t[0]); fp_sub(t[0], unconst_fp_int(curve->q), t[1]); fp_copy(t[fp_cmp_d(t[1], 0) != FP_LT], c); memset(t, 0, sizeof(t)); } static inline void ff_sub(const ecdsa_curve_t * const curve, const fp_int * const a, const fp_int * const b, fp_int *c) { fp_int t[2][1] = {INIT_FP_INT}; fp_sub(unconst_fp_int(a), unconst_fp_int(b), t[0]); fp_add(t[0], unconst_fp_int(curve->q), t[1]); fp_copy(t[fp_cmp_d(t[0], 0) == FP_LT], c); memset(t, 0, sizeof(t)); } static inline void ff_mul(const ecdsa_curve_t * const curve, const fp_int * const a, const fp_int * const b, fp_int *c) { fp_mul(unconst_fp_int(a), unconst_fp_int(b), c); fp_montgomery_reduce(c, unconst_fp_int(curve->q), curve->rho); } static inline void ff_sqr(const ecdsa_curve_t * const curve, const fp_int * const a, fp_int *b) { fp_sqr(unconst_fp_int(a), b); fp_montgomery_reduce(b, unconst_fp_int(curve->q), curve->rho); } /* * Test whether a point is the point at infinity. * * In Jacobian projective coordinate, any point of the form * * (j ** 2, j **3, 0) for j in [1..q-1] * * is on the line at infinity, but for practical purposes simply * checking the z coordinate is probably sufficient. */ static inline int point_is_infinite(const ec_point_t * const P) { return P == NULL || fp_iszero(P->z); } /* * Set a point to be the point at infinity. For Jacobian projective * coordinates, it's customary to use (1 : 1 : 0) as the * representitive value. * * If a curve is supplied, we want the Montgomery form of the point at * infinity for that curve. */ static inline hal_error_t point_set_infinite(ec_point_t *P, const ecdsa_curve_t * const curve) { hal_assert(P != NULL); if (curve != NULL) { fp_copy(unconst_fp_int(curve->mu), P->x); fp_copy(unconst_fp_int(curve->mu), P->y); fp_zero(P->z); } else { fp_set(P->x, 1); fp_set(P->y, 1); fp_zero(P->z); } return HAL_OK; } /* * Copy a point. */ static inline void point_copy(const ec_point_t * const P, ec_point_t *R) { if (P != NULL && R != NULL && P != R) *R = *P; } /** * Convert a point into Montgomery form. * @param P [in/out] The point to map * @param curve The curve parameters structure */ static inline hal_error_t point_to_montgomery(ec_point_t *P, const ecdsa_curve_t * const curve) { hal_assert(P != NULL && curve != NULL); if (fp_cmp_d(unconst_fp_int(P->z), 1) != FP_EQ) return HAL_ERROR_BAD_ARGUMENTS; if (fp_mulmod(P->x, unconst_fp_int(curve->mu), unconst_fp_int(curve->q), P->x) != FP_OKAY || fp_mulmod(P->y, unconst_fp_int(curve->mu), unconst_fp_int(curve->q), P->y) != FP_OKAY) return HAL_ERROR_IMPOSSIBLE; fp_copy(unconst_fp_int(curve->mu), P->z); return HAL_OK; } /** * Map a point in projective Jacbobian coordinates back to affine * space. This also converts back from Montgomery to plain form. * @param P [in/out] The point to map * @param curve The curve parameters structure * * It's not possible to represent the point at infinity in plain * affine coordinates, and the calling function will have to handle * the point at infinity specially in any case, so we declare this to * be the calling function's problem. */ static inline hal_error_t point_to_affine(ec_point_t *P, const ecdsa_curve_t * const curve) { hal_assert(P != NULL && curve != NULL); if (point_is_infinite(P)) return HAL_ERROR_IMPOSSIBLE; hal_error_t err = HAL_ERROR_IMPOSSIBLE; fp_int t1[1] = INIT_FP_INT; fp_int t2[1] = INIT_FP_INT; fp_int * const q = unconst_fp_int(curve->q); fp_montgomery_reduce(P->z, q, curve->rho); if (fp_invmod (P->z, q, t1) != FP_OKAY || /* t1 = 1 / z */ fp_sqrmod (t1, q, t2) != FP_OKAY || /* t2 = 1 / z**2 */ fp_mulmod (t1, t2, q, t1) != FP_OKAY) /* t1 = 1 / z**3 */ goto fail; fp_mul (P->x, t2, P->x); /* x = x / z**2 */ fp_mul (P->y, t1, P->y); /* y = y / z**3 */ fp_set (P->z, 1); /* z = 1 */ fp_montgomery_reduce(P->x, q, curve->rho); fp_montgomery_reduce(P->y, q, curve->rho); err = HAL_OK; fail: fp_zero(t1); fp_zero(t2); return err; } /** * Double an EC point. * @param P The point to double * @param R [out] The destination of the double * @param curve The curve parameters structure * * Algorithm is dbl-2001-b from * http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html */ static inline hal_error_t point_double(const ec_point_t * const P, ec_point_t *R, const ecdsa_curve_t * const curve) { hal_assert(P != NULL && R != NULL && curve != NULL); const int was_infinite = point_is_infinite(P); fp_int alpha[1] = INIT_FP_INT; fp_int beta[1] = INIT_FP_INT; fp_int gamma[1] = INIT_FP_INT; fp_int delta[1] = INIT_FP_INT; fp_int t1[1] = INIT_FP_INT; fp_int t2[1] = INIT_FP_INT; ff_sqr (curve, P->z, delta); /* delta = Pz ** 2 */ ff_sqr (curve, P->y, gamma); /* gamma = Py ** 2 */ ff_mul (curve, P->x, gamma, beta); /* beta = Px * gamma */ ff_sub (curve, P->x, delta, t1); /* alpha = 3 * (Px - delta) * (Px + delta) */ ff_add (curve, P->x, delta, t2); ff_mul (curve, t1, t2, t1); ff_add (curve, t1, t1, t2); ff_add (curve, t1, t2, alpha); ff_sqr (curve, alpha, t1); /* Rx = (alpha ** 2) - (8 * beta) */ ff_add (curve, beta, beta, t2); ff_add (curve, t2, t2, t2); ff_add (curve, t2, t2, t2); ff_sub (curve, t1, t2, R->x); ff_add (curve, P->y, P->z, t1); /* Rz = ((Py + Pz) ** 2) - gamma - delta */ ff_sqr (curve, t1, t1); ff_sub (curve, t1, gamma, t1); ff_sub (curve, t1, delta, R->z); ff_add (curve, beta, beta, t1); /* Ry = (((4 * beta) - Rx) * alpha) - (8 * (gamma ** 2)) */ ff_add (curve, t1, t1, t1); ff_sub (curve, t1, R->x, t1); ff_mul (curve, t1, alpha, t1); ff_sqr (curve, gamma, t2); ff_add (curve, t2, t2, t2); ff_add (curve, t2, t2, t2); ff_add (curve, t2, t2, t2); ff_sub (curve, t1, t2, R->y); hal_assert(was_infinite == point_is_infinite(P)); fp_zero(alpha); fp_zero(beta); fp_zero(gamma); fp_zero(delta); fp_zero(t1); fp_zero(t2); return HAL_OK; } /** * Add two EC points * @param P The point to add * @param Q The point to add * @param R [out] The destination of the double * @param curve The curve parameters structure * * Algorithm is madd-2007-bl from * http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html * * The special cases are unfortunate, but are probably unavoidable for * this type of curve. We do what we can to make this constant-time * in spite of the special cases. The one we really can't do much * about is P == Q, because in that case we have to switch to the * point doubling algorithm. */ static inline hal_error_t point_add(const ec_point_t * const P, const ec_point_t * const Q, ec_point_t *R, const ecdsa_curve_t * const curve) { hal_assert(P != NULL && Q != NULL && R != NULL && curve != NULL); /* * Q must be affine in Montgomery form. */ hal_assert(fp_cmp(unconst_fp_int(Q->z), unconst_fp_int(curve->mu)) == FP_EQ); #warning What happens here if P and Q are not equal but map to the same point in affine space? const int same_xz = (fp_cmp(unconst_fp_int(P->z), unconst_fp_int(Q->z)) == FP_EQ && fp_cmp(unconst_fp_int(P->x), unconst_fp_int(Q->x)) == FP_EQ); /* * If P == Q, we must use point doubling instead of point addition, * and there's nothing we can do to mask the timing differences. * So just do it, right away. */ if (same_xz && fp_cmp(unconst_fp_int(P->y), unconst_fp_int(Q->y)) == FP_EQ) return point_double(P, R, curve); /* * Check now for the other special cases, but defer handling them * until the end, to mask timing differences. */ const int P_was_infinite = point_is_infinite(P); fp_int Qy_neg[1] = INIT_FP_INT; fp_sub(unconst_fp_int(curve->q), unconst_fp_int(Q->y), Qy_neg); const int result_is_infinite = fp_cmp(unconst_fp_int(P->y), Qy_neg) == FP_EQ && same_xz; fp_zero(Qy_neg); /* * Main point addition algorithm. */ fp_int Z1Z1[1] = INIT_FP_INT; fp_int H[1] = INIT_FP_INT; fp_int HH[1] = INIT_FP_INT; fp_int I[1] = INIT_FP_INT; fp_int J[1] = INIT_FP_INT; fp_int r[1] = INIT_FP_INT; fp_int V[1] = INIT_FP_INT; fp_int t[1] = INIT_FP_INT; ff_sqr (curve, P->z, Z1Z1); /* Z1Z1 = Pz ** 2 */ ff_mul (curve, Q->x, Z1Z1, H); /* H = (Qx * Z1Z1) - Px */ ff_sub (curve, H, P->x, H); ff_sqr (curve, H, HH); /* HH = H ** 2 */ ff_add (curve, HH, HH, I); /* I = 4 * HH */ ff_add (curve, I, I, I); ff_mul (curve, H, I, J); /* J = H * I */ ff_mul (curve, P->z, Z1Z1, r); /* r = 2 * ((Qy * Pz * Z1Z1) - Py) */ ff_mul (curve, Q->y, r, r); ff_sub (curve, r, P->y, r); ff_add (curve, r, r, r); ff_mul (curve, P->x, I, V); /* V = Px * I */ ff_sqr (curve, r, R->x); /* Rx = (r ** 2) - J - (2 * V) */ ff_sub (curve, R->x, J, R->x); ff_sub (curve, R->x, V, R->x); ff_sub (curve, R->x, V, R->x); ff_mul (curve, P->y, J, t); /* Ry = (r * (V - Rx)) - (2 * Py * J) */ ff_sub (curve, V, R->x, R->y); ff_mul (curve, r, R->y, R->y); ff_sub (curve, R->y, t, R->y); ff_sub (curve, R->y, t, R->y); ff_add (curve, P->z, H, R->z); /* Rz = ((Pz + H) ** 2) - Z1Z1 - HH */ ff_sqr (curve, R->z, R->z); ff_sub (curve, R->z, Z1Z1, R->z); ff_sub (curve, R->z, HH, R->z); fp_zero(Z1Z1), fp_zero(H), fp_zero(HH), fp_zero(I), fp_zero(J), fp_zero(r), fp_zero(V), fp_zero(t); /* * Handle deferred special cases, then we're done. */ if (P_was_infinite) point_copy(Q, R); else if (result_is_infinite) point_set_infinite(R, curve); return HAL_OK; } /** * Perform a point multiplication. * @param k The scalar to multiply by * @param P The base point * @param R [out] Destination for kP * @param curve Curve parameters * @return HAL_OK on success * * P must be in affine form. */
/*
 * ks_mmap.c
 * ---------
 * Keystore implementation over POSIX mmap().
 *
 * Authors: Rob Austein
 * Copyright (c) 2015, NORDUnet A/S All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 * - Redistributions of source code must retain the above copyright notice,
 *   this list of conditions and the following disclaimer.
 *
 * - 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.
 *
 * - Neither the name of the NORDUnet nor the names of its contributors may
 *   be used to endorse or promote products derived from this software
 *   without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT
 * HOLDER OR CONTRIBUTORS 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 <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <string.h>
#include <sys/errno.h>
#include <unistd.h>

#include "hal.h"
#include "hal_internal.h"

#ifndef HAL_KS_MMAP_FILE
#define HAL_KS_MMAP_FILE ".cryptech_hal_keystore"
#endif

#ifndef MAP_FILE
#define MAP_FILE 0
#endif

/*
 * Storing the KEK in with the keys it's protecting is a bad idea, but we have no better
 * place to put it (real protection requires dedicated hardware, which we don't have here).
 */

#define KEKBUF_LEN (bitsToBytes(256))

static hal_ks_keydb_t *db;
static uint8_t *kekbuf;

const hal_ks_keydb_t *hal_ks_get_keydb(void)
{
  if (db != NULL)
    return db;

  const char * const env  = getenv("CRYPTECH_KEYSTORE");
  const char * const home = getenv("HOME");
  const char * const base = HAL_KS_MMAP_FILE;
  const long pagemask = sysconf(_SC_PAGESIZE) - 1;
  const size_t len = (sizeof(hal_ks_keydb_t) + KEKBUF_LEN + pagemask) & ~pagemask;

  char fn_[strlen(base) + (home == NULL ? 0 : strlen(home)) + 2];
  const char *fn = fn_;
  int fd;

  if (pagemask < 0)
    return NULL;

  if (env != NULL)
    fn = env;
  else if (home == NULL)
    fn = base;
  else
    strcat(strcat(strcpy(fn_, home), "/"), base);

  if ((fd = open(fn, O_RDWR | O_CREAT | O_EXCL, 0600)) >= 0) {
    uint8_t zeros[len];
    memset(zeros, 0, sizeof(zeros));
    (void) write(fd, zeros, sizeof(zeros));
  }
  else if (errno == EEXIST) {
    fd = open(fn, O_RDWR | O_CREAT, 0600);
  }

  if (fd >= 0 && (db = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_FILE | MAP_SHARED, fd, 0)) != NULL)
    kekbuf = (uint8_t *) (db + 1);

  (void) close(fd);

  return db;
}

hal_error_t hal_ks_set_keydb(const hal_ks_key_t * const key,
                             const int loc,
                             const int updating)
{
  if (key == NULL || loc < 0 || loc >= sizeof(db->keys)/sizeof(*db->keys) || (!key->in_use != !updating))
    return HAL_ERROR_BAD_ARGUMENTS;

  db->keys[loc] = *key;
  db->keys[loc].in_use = 1;
  return HAL_OK;
}

hal_error_t hal_ks_del_keydb(const int loc)
{
  if (loc < 0 || loc >= sizeof(db->keys)/sizeof(*db->keys))
    return HAL_ERROR_BAD_ARGUMENTS;

  db->keys[loc].in_use = 0;
  memset(&db->keys[loc], 0, sizeof(db->keys[loc]));
  return HAL_OK;
}

hal_error_t hal_ks_set_pin(const hal_user_t user,
                           const hal_ks_pin_t * const pin)
{
  if (pin == NULL)
    return HAL_ERROR_BAD_ARGUMENTS;

  hal_ks_pin_t *p = NULL;

  switch (user) {
  case HAL_USER_WHEEL:  p = &db->wheel_pin;  break;
  case HAL_USER_SO:	p = &db->so_pin;     break;
  case HAL_USER_NORMAL:	p = &db->user_pin;   break;
  default:		return HAL_ERROR_BAD_ARGUMENTS;
  }

  *p = *pin;
  return HAL_OK;
}

hal_error_t hal_get_kek(uint8_t *kek,
                           size_t *kek_len,
                           const size_t kek_max)
{
  if (kek == NULL || kek_len == NULL || kek_max < bitsToBytes(128))
    return HAL_ERROR_BAD_ARGUMENTS;

  if (kekbuf == NULL)
    return HAL_ERROR_IMPOSSIBLE;

  hal_error_t err;

  const size_t len = ((kek_max < bitsToBytes(192)) ? bitsToBytes(128) :
                      (kek_max < bitsToBytes(256)) ? bitsToBytes(192) :
                      bitsToBytes(256));

  uint8_t t = 0;

  for (int i = 0; i < KEKBUF_LEN; i++)
    t |= kekbuf[i];

  if (t == 0 && (err = hal_rpc_get_random(kekbuf, sizeof(KEKBUF_LEN))) != HAL_OK)
    return err;

  memcpy(kek, kekbuf, len);
  *kek_len = len;
  return HAL_OK;
}

/*
 * Local variables:
 * indent-tabs-mode: nil
 * End:
 */