diff options
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | GNUmakefile | 8 | ||||
-rw-r--r-- | README.md | 67 | ||||
-rw-r--r-- | asn1.c | 227 | ||||
-rw-r--r-- | asn1_internal.h | 108 | ||||
-rw-r--r-- | csprng.c | 50 | ||||
-rw-r--r-- | ecdsa.c | 1545 | ||||
-rw-r--r-- | ecdsa_curves.h | 92 | ||||
-rw-r--r-- | hal.h | 142 | ||||
-rw-r--r-- | hal_io_eim.c | 6 | ||||
-rw-r--r-- | hal_io_i2c.c | 6 | ||||
-rw-r--r-- | hash.c | 195 | ||||
-rw-r--r-- | novena-eim.c | 16 | ||||
-rw-r--r-- | pbkdf2.c | 2 | ||||
-rw-r--r-- | rsa.c | 353 | ||||
-rw-r--r-- | tests/GNUmakefile | 2 | ||||
-rw-r--r-- | tests/test-ecdsa.c | 326 | ||||
-rw-r--r-- | tests/test-ecdsa.h | 329 | ||||
-rw-r--r-- | tests/test-ecdsa.py | 156 | ||||
-rw-r--r-- | tests/test-hash.c | 4 | ||||
-rw-r--r-- | tests/test-pbkdf2.c | 2 | ||||
-rw-r--r-- | tests/test-rsa.c | 4 |
22 files changed, 3215 insertions, 426 deletions
@@ -6,6 +6,7 @@ autom4te.cache config.log config.status tests/test-aes-key-wrap +tests/test-ecdsa tests/test-hash tests/test-pbkdf2 tests/test-rsa diff --git a/GNUmakefile b/GNUmakefile index 82ec7ec..f425c50 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -28,7 +28,7 @@ INC = hal.h LIB = libhal.a OBJ = ${IO_OBJ} csprng.o hash.o aes_keywrap.o pbkdf2.o \ - modexp.o rsa.o errorstrings.o + modexp.o rsa.o ecdsa.o asn1.o errorstrings.o IO_OBJ_EIM = hal_io_eim.o novena-eim.o IO_OBJ_I2C = hal_io_i2c.o @@ -37,7 +37,7 @@ IO_OBJ_I2C = hal_io_i2c.o IO_OBJ = ${IO_OBJ_EIM} TFMDIR := $(abspath ../thirdparty/libtfm) -CFLAGS := -g3 -Wall -fPIC -std=c99 -I${TFMDIR} +CFLAGS := -g3 -Wall -fPIC -std=c99 -I${TFMDIR} -DHAL_ECDSA_DEBUG_ONLY_STATIC_TEST_VECTOR_RANDOM=1 LDFLAGS := -g3 -L${TFMDIR} -ltfm all: ${LIB} @@ -49,6 +49,10 @@ ${OBJ}: ${INC} ${LIB}: ${OBJ} ar rcs $@ $^ +asn1.o rsa.o ecdsa.o: asn1_internal.h + +ecdsa.o: ecdsa_curves.h + test: all cd tests; ${MAKE} -k $@ @@ -1,6 +1,8 @@ libhal ====== +## Overview ## + This library combines a set of low-level API functions which talk to the Cryptech FPGA cores with a set of higher-level functions providing various cryptographic services. @@ -25,16 +27,21 @@ Current contents of the library: * An implementation of RSA using the Cryptech ModExp core. +* An implementation of ECDSA, currently entirely in software. + * Test code for all of the above. Most of these are fairly well self-contained, although the PBKDF2 implementation uses the hash-core-based HMAC implementation. -The major exception is the RSA implementation, which uses an external -bignum implementation (libtfm) to handle a lot of the arithmetic. In -the long run, much or all of this may end up being implemented in -Verilog, but for the moment all of the RSA math except for modular -exponentiation is happening in software. +The major exceptions are the RSA and ECDSA implementations, which uses +an external bignum implementation (libtfm) to handle a lot of the +arithmetic. In the long run, much or all of this may end up being +implemented in Verilog, but for the moment all of the RSA math except +for modular exponentiation is happening in software, as is all of the +math for ECDSA. + +## RSA ## The RSA implementation includes a compile-time option to bypass the ModExp core and do everything in software, because the ModExp core is @@ -44,3 +51,53 @@ The RSA implementation includes optional blinding (enabled by default) and just enough ASN.1 code to read and write private keys; the expectation is that the latter will be used in combination with the AES Key Wrap code. + +## ECDSA ## + +The ECDSA implementation is specific to the NIST prime curves P-256, +P-384, and P-521. + +The ECDSA implementation includes a compile-time option to allow test +code to bypass the CSPRNG in order to test against known test vectors. +Do **NOT** enable this in production builds, as ECDSA depends on good +random numbers not just for private keys but for individual +signatures, and an attacker who knows the random number used for a +particular signature can use this to recover the private key. +Arguably, this option should be removed from the code entirely, once +the implementation is stable. + +The ECDSA implementation includes enough ASN.1 to read and write ECDSA +signatures and ECDSA private keys in RFC 5915 format; the expectation +is that the latter will be used in combination with AES Key Wrap. + +The ECDSA implementation attempts to be constant-time, to reduce the +risk of timing channel attacks. The algorithms chosen for the point +arithmetic are a tradeoff between speed and code complexity, and can +probably be improved upon even in software; reimplementing at least +the field arithmetic in hardware would probably also help. + +The current point addition and point doubling algorithms come from the +[EFD][]. At least at the moment, we're only interested in ECDSA with +the NIST prime curves, so we use algorithms optimized for a=-3. + +The point multiplication algorithm is a Montgomery Ladder, which is +not the fastest possible algorithm, but is relatively easy to confirm +by inspection as constant-time. Point multiplication could probably +be made faster by using a non-adjacent form (NAF) representation for +the scalar, but the author doesn't yet understand that well enough to +implement it as a constant-time algorithm. In theory, changing to a +NAF representation could be done without any change to the public API. + +Points stored in keys and curve parameters are in affine format, but +all point arithmetic is performed in Jacobian projective coordinates, +with the coordinates in Montgomery form; final mapping back to affine +coordinates also handles the final Montgomery reduction. + +## API ## + +Yeah, we ought to document the API, Real Soon Now, perhaps using +[Doxygen][]. For the moment, see the function prototypes in hal.h and +comments in the code. + +[EFD]: http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html +[Doxygen]: http://www.doxygen.org/ @@ -0,0 +1,227 @@ +/* + * asn1.c + * ------ + * Minimal ASN.1 implementation in support of Cryptech libhal. + * + * The functions in this module are not intended to be part of the + * public API. Rather, these are utility functions used by more than + * one module within the library, which would otherwise have to be + * duplicated. The main reason for keeping these private is to avoid + * having the public API depend on any details of the underlying + * bignum implementation (currently libtfm, but that might change). + * + * As of this writing, the ASN.1 support we need is quite minimal, so, + * rather than attempting to clean all the unecessary cruft out of a + * general purpose ASN.1 implementation, we hand code the very small + * number of data types we need. At some point this will probably + * become impractical, at which point we might want to look into using + * something like the asn1c compiler. + * + * Authors: Rob Austein + * Copyright (c) 2015, SUNET + * + * 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 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 OWNER 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 <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <stddef.h> +#include <string.h> +#include <assert.h> + +#include "hal.h" + +#include "asn1_internal.h" + +/* + * Encode tag and length fields of an ASN.1 object. + * + * Sets *der_len to the size of of the ASN.1 header (tag and length + * fields); caller supplied length of value field, so presumably + * already knows it. + * + * If der is NULL, just return the size of the header that would be + * encoded and returns HAL_OK. + * + * If der isn't NULL, returns HAL_ERROR_RESULT_TOO_LONG unless full + * header plus value will fit; this is a bit weird, but is useful when + * using this to construct encoders for complte ASN.1 objects. + */ + +hal_error_t hal_asn1_encode_header(const uint8_t tag, + const size_t value_len, + uint8_t *der, size_t *der_len, const size_t der_max) +{ + size_t header_len = 2; /* Shortest encoding is one octet each for tag and length */ + + if (value_len >= 128) /* Add octets for longer length encoding as needed */ + for (size_t n = value_len; n > 0; n >>= 8) + ++header_len; + + if (der_len != NULL) + *der_len = header_len; + + if (der == NULL) /* If caller just wanted the length, we're done */ + return HAL_OK; + + /* + * Make sure there's enough room for header + value, then encode. + */ + + if (value_len + header_len > der_max) + return HAL_ERROR_RESULT_TOO_LONG; + + *der++ = tag; + + if (value_len < 128) { + *der = (uint8_t) value_len; + } + + else { + *der = 0x80 | (uint8_t) (header_len -= 2); + for (size_t n = value_len; n > 0 && header_len > 0; n >>= 8) + der[header_len--] = (uint8_t) (n & 0xFF); + } + + return HAL_OK; +} + +/* + * Encode an unsigned ASN.1 INTEGER from a libtfm bignum. If der is + * NULL, just return the length of what we would have encoded. + */ + +hal_error_t hal_asn1_encode_integer(const fp_int * const bn, + uint8_t *der, size_t *der_len, const size_t der_max) +{ + if (bn == NULL) + return HAL_ERROR_BAD_ARGUMENTS; + + /* + * We only handle unsigned INTEGERs, so we need to pad data with a + * leading zero if the most significant bit is set, to avoid + * flipping the ASN.1 sign bit. Conveniently, this also handles the + * difference between libtfm's and ASN.1's encoding of zero. + */ + + if (fp_cmp_d(unconst_fp_int(bn), 0) == FP_LT) + return HAL_ERROR_BAD_ARGUMENTS; + + const int leading_zero = fp_iszero(bn) || (fp_count_bits(unconst_fp_int(bn)) & 7) == 0; + const size_t vlen = fp_unsigned_bin_size(unconst_fp_int(bn)) + leading_zero; + hal_error_t err; + size_t hlen; + + err = hal_asn1_encode_header(ASN1_INTEGER, vlen, der, &hlen, der_max); + + if (der_len != NULL) + *der_len = hlen + vlen; + + if (der == NULL || err != HAL_OK) + return err; + + assert(hlen + vlen <= der_max); + + der += hlen; + if (leading_zero) + *der++ = 0x00; + fp_to_unsigned_bin(unconst_fp_int(bn), der); + + return HAL_OK; +} + +/* + * Parse tag and length of an ASN.1 object. Tag must match value + * specified by the caller. On success, sets hlen and vlen to lengths + * of header and value, respectively. + */ + +hal_error_t hal_asn1_decode_header(const uint8_t tag, + const uint8_t * const der, size_t der_max, + size_t *hlen, size_t *vlen) +{ + assert(der != NULL && hlen != NULL && vlen != NULL); + + if (der_max < 2 || der[0] != tag) + return HAL_ERROR_ASN1_PARSE_FAILED; + + if ((der[1] & 0x80) == 0) { + *hlen = 2; + *vlen = der[1]; + } + + else { + *hlen = 2 + (der[1] & 0x7F); + *vlen = 0; + + if (*hlen > der_max) + return HAL_ERROR_ASN1_PARSE_FAILED; + + for (size_t i = 2; i < *hlen; i++) + *vlen = (*vlen << 8) + der[i]; + } + + if (*hlen + *vlen > der_max) + return HAL_ERROR_ASN1_PARSE_FAILED; + + return HAL_OK; +} + +/* + * Decode an ASN.1 INTEGER into a libtfm bignum. Since we only + * support (or need to support, or expect to see) unsigned integers, + * we return failure if the sign bit is set in the ASN.1 INTEGER. + */ + +hal_error_t hal_asn1_decode_integer(fp_int *bn, + const uint8_t * const der, size_t *der_len, const size_t der_max) +{ + if (bn == NULL || der == NULL) + return HAL_ERROR_BAD_ARGUMENTS; + + hal_error_t err; + size_t hlen, vlen; + + if ((err = hal_asn1_decode_header(ASN1_INTEGER, der, der_max, &hlen, &vlen)) != HAL_OK) + return err; + + if (der_len != NULL) + *der_len = hlen + vlen; + + if (vlen < 1 || (der[hlen] & 0x80) != 0x00) + return HAL_ERROR_ASN1_PARSE_FAILED; + + fp_init(bn); + fp_read_unsigned_bin(bn, (uint8_t *) der + hlen, vlen); + return HAL_OK; +} + +/* + * Local variables: + * indent-tabs-mode: nil + * End: + */ diff --git a/asn1_internal.h b/asn1_internal.h new file mode 100644 index 0000000..f6e470f --- /dev/null +++ b/asn1_internal.h @@ -0,0 +1,108 @@ +/* + * asn1.h + * ------ + * Library internal header file for ASN.1 routines. + * + * These functions are not part of the public libhal API. + * + * More than 20 years after it was written, the best simple + * introduction to ASN.1 is still Burt Kalski's "A Layman's Guide to a + * Subset of ASN.1, BER, and DER". Ask your nearest search engine. + * + * Authors: Rob Austein + * Copyright (c) 2015, SUNET + * + * 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 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 OWNER 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. + */ + +#ifndef _HAL_ASN1_H_ +#define _HAL_ASN1_H_ + +#include <stdint.h> +#include <stdlib.h> + +#include <tfm.h> + +#define ASN1_UNIVERSAL 0x00 +#define ASN1_APPLICATION 0x40 +#define ASN1_CONTEXT_SPECIFIC 0x80 +#define ASN1_PRIVATE 0xC0 + +#define ASN1_PRIMITIVE 0x00 +#define ASN1_CONSTRUCTED 0x20 + +#define ASN1_TAG_MASK 0x1F + +#define ASN1_INTEGER (ASN1_PRIMITIVE | 0x02) +#define ASN1_BIT_STRING (ASN1_PRIMITIVE | 0x03) +#define ASN1_OCTET_STRING (ASN1_PRIMITIVE | 0x04) +#define ASN1_NULL (ASN1_PRIMITIVE | 0x05) +#define ASN1_OBJECT_IDENTIFIER (ASN1_PRIMITIVE | 0x06) +#define ASN1_SEQUENCE (ASN1_CONSTRUCTED | 0x10) +#define ASN1_SET (ASN1_CONSTRUCTED | 0x11) + +#define ASN1_EXPLICIT_CONTEXT (ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED) +#define ASN1_EXPLICIT_0 (ASN1_EXPLICIT_CONTEXT + 0) +#define ASN1_EXPLICIT_1 (ASN1_EXPLICIT_CONTEXT + 1) + +/* + * Functions to strip const qualifiers from arguments to libtfm calls + * in a relatively type-safe manner. These don't really have anything + * to do with ASN.1 per se, but all the code that needs them reads + * this header file, so this is the simplest place to put them. + */ + +static inline fp_int *unconst_fp_int(const fp_int * const arg) +{ + return (fp_int *) arg; +} + +static inline uint8_t *unconst_uint8_t(const uint8_t * const arg) +{ + return (uint8_t *) arg; +} + +extern hal_error_t hal_asn1_encode_header(const uint8_t tag, + const size_t value_len, + uint8_t *der, size_t *der_len, const size_t der_max); + +extern hal_error_t hal_asn1_decode_header(const uint8_t tag, + const uint8_t * const der, size_t der_max, + size_t *hlen, size_t *vlen); + +extern hal_error_t hal_asn1_encode_integer(const fp_int * const bn, + uint8_t *der, size_t *der_len, const size_t der_max); + +extern hal_error_t hal_asn1_decode_integer(fp_int *bn, + const uint8_t * const der, size_t *der_len, const size_t der_max); + +#endif /* _HAL_ASN1_H_ */ + +/* + * Local variables: + * indent-tabs-mode: nil + * End: + */ @@ -1,34 +1,34 @@ -/* +/* * csprng.c * -------- * HAL interface to Cryptech CSPRNG. - * + * * Authors: Joachim Strömbergson, Paul Selkirk, Rob Austein * Copyright (c) 2014-2015, SUNET - * - * 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 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 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * + * 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 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 OWNER 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 + * 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. */ @@ -0,0 +1,1545 @@ +/* + * 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, SUNET + * + * 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 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 OWNER 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 <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <stddef.h> +#include <string.h> +#include <assert.h> + +#include "hal.h" +#include <tfm.h> +#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 + +/* + * 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 */ +} 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. + */ + +typedef struct { + fp_int x[1], y[1], z[1]; +} ec_point_t; + +struct hal_ecdsa_key { + hal_ecdsa_key_type_t type; /* Public or private is */ + hal_ecdsa_curve_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); + +/* + * Error handling. + */ + +#define lose(_code_) do { err = _code_; goto fail; } while (0) + +/* + * We can't (usefully) initialize fp_int variables 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 * const get_curve(const hal_ecdsa_curve_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); + + 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); + + 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); + + initialized = 1; + } + + switch (curve) { + case HAL_ECDSA_CURVE_P256: return &curve_p256; + case HAL_ECDSA_CURVE_P384: return &curve_p384; + case HAL_ECDSA_CURVE_P521: return &curve_p521; + default: return NULL; + } +} + +/* + * 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]; + memset(t, 0, sizeof(t)); + + 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]; + memset(t, 0, sizeof(t)); + + 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) +{ + assert(P != NULL); + return 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. + */ + +static inline void point_set_infinite(ec_point_t *P) +{ + assert(P != NULL); + fp_set(P->x, 1); + fp_set(P->y, 1); + fp_set(P->z, 0); +} + +/* + * 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; +} + +/** + * 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 void point_double(const ec_point_t * const P, + ec_point_t *R, + const ecdsa_curve_t * const curve) +{ + assert(P != NULL && R != NULL && curve != NULL); + + assert(!point_is_infinite(P)); + + fp_int alpha[1], beta[1], gamma[1], delta[1], t1[1], t2[1]; + + fp_init(alpha); fp_init(beta); fp_init(gamma); fp_init(delta); fp_init(t1); fp_init(t2); + + 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); + + fp_zero(alpha); fp_zero(beta); fp_zero(gamma); fp_zero(delta); fp_zero(t1); fp_zero(t2); +} + +/** + * 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 add-2007-bl from + * http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html + * + * The special cases for P == Q and P == -Q are unfortunate, but are + * probably unavoidable for this type of curve. + */ + +static inline void point_add(const ec_point_t * const P, + const ec_point_t * const Q, + ec_point_t *R, + const ecdsa_curve_t * const curve) +{ + assert(P != NULL && Q != NULL && R != NULL && curve != NULL); + + if (fp_cmp(unconst_fp_int(P->x), unconst_fp_int(Q->x)) == FP_EQ && + fp_cmp(unconst_fp_int(P->z), unconst_fp_int(Q->z)) == FP_EQ) { + + /* + * If P == Q, we have to use the doubling algorithm instead. + */ + + if (fp_cmp(unconst_fp_int(P->y), unconst_fp_int(Q->y)) == FP_EQ) + return point_double(P, R, curve); + + fp_int Qy_neg[1]; + fp_sub(unconst_fp_int(curve->q), unconst_fp_int(Q->y), Qy_neg); + const int zero_sum = fp_cmp(unconst_fp_int(P->y), Qy_neg) == FP_EQ; + fp_zero(Qy_neg); + + /* + * If P == -Q, P + Q is the point at infinity. Which can't be + * expressed in affine coordinates, but that's not this function's + * problem. + */ + + if (zero_sum) + return point_set_infinite(R); + } + + fp_int Z1Z1[1], Z2Z2[1], U1[1], U2[1], S1[1], S2[1], H[1], I[1], J[1], r[1], V[1], t[1]; + + fp_init(Z1Z1), fp_init(Z2Z2), fp_init(U1), fp_init(U2), fp_init(S1), fp_init(S2); + fp_init(H), fp_init(I), fp_init(J), fp_init(r), fp_init(V), fp_init(t); + + ff_sqr (curve, P->z, Z1Z1); /* Z1Z1 = Pz ** 2 */ + + ff_sqr (curve, Q->z, Z2Z2); /* Z2Z1 = Qz ** 2 */ + + ff_mul (curve, P->x, Z2Z2, U1); /* U1 = Px * Z2Z2 */ + + ff_mul (curve, Q->x, Z1Z1, U2); /* U2 = Qx * Z1Z1 */ + + ff_mul (curve, Q->z, Z2Z2, S1); /* S1 = Py * (Qz ** 3) */ + ff_mul (curve, P->y, S1, S1); + + ff_mul (curve, P->z, Z1Z1, S2); /* S2 = Qy * (Pz ** 3) */ + ff_mul (curve, Q->y, S2, S2); + + ff_sub (curve, U2, U1, H); /* H = U2 - U1 */ + + ff_add (curve, H, H, I); /* I = (2 * H) ** 2 */ + ff_sqr (curve, I, I); + + ff_mul (curve, H, I, J); /* J = H * I */ + + ff_sub (curve, S2, S1, r); /* r = 2 * (S2 - S1) */ + ff_add (curve, r, r, r); + + ff_mul (curve, U1, I, V); /* V = U1 * 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_sub (curve, V, R->x, R->y); /* Ry = (r * (V - Rx)) - (2 * S1 * J) */ + ff_mul (curve, r, R->y, R->y); + ff_mul (curve, S1, J, t); + ff_sub (curve, R->y, t, R->y); + ff_sub (curve, R->y, t, R->y); + + ff_add (curve, P->z, Q->z, R->z); /* Rz = (((Pz + Qz) ** 2) - Z1Z1 - Z2Z2) * H */ + ff_sqr (curve, R->z, R->z); + ff_sub (curve, R->z, Z1Z1, R->z); + ff_sub (curve, R->z, Z2Z2, R->z); + ff_mul (curve, R->z, H, R->z); + + fp_zero(Z1Z1), fp_zero(Z2Z2), fp_zero(U1), fp_zero(U2), fp_zero(S1), fp_zero(S2); + fp_zero(H), fp_zero(I), fp_zero(J), fp_zero(r), fp_zero(V), fp_zero(t); +} + +/** + * Map a point in projective Jacbobian coordinates back to affine space + * @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 affine + * coordinates, and the calling function will have to handle this + * 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) +{ + 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]; fp_init(t1); + fp_int t2[1]; fp_init(t2); + + 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; +} + +/** + * 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 + * @param map Boolean whether to map back to affine (1: map, 0: leave projective) + * @return HAL_OK on success + * + * This implementation uses the "Montgomery Ladder" approach, which is + * relatively robust against timing channel attacks if nothing else + * goes wrong, but many other things can indeed go wrong. + */ + +static hal_error_t point_scalar_multiply(const fp_int * const k, + const ec_point_t * const P, + ec_point_t *R, + const ecdsa_curve_t * const curve, + const int map) +{ + assert(k != NULL && P != NULL && R != NULL && curve != NULL); + + if (fp_iszero(k)) + return HAL_ERROR_BAD_ARGUMENTS; + + /* + * Convert to Montgomery form and initialize table. Initial values: + * + * M[0] = 1P + * M[1] = 2P + * M[2] = don't care, only used for timing-attack resistance + */ + + ec_point_t M[3][1]; + memset(M, 0, sizeof(M)); + + if (fp_mulmod(unconst_fp_int(P->x), unconst_fp_int(curve->mu), unconst_fp_int(curve->q), M[0]->x) != FP_OKAY || + fp_mulmod(unconst_fp_int(P->y), unconst_fp_int(curve->mu), unconst_fp_int(curve->q), M[0]->y) != FP_OKAY || + fp_mulmod(unconst_fp_int(P->z), unconst_fp_int(curve->mu), unconst_fp_int(curve->q), M[0]->z) != FP_OKAY) { + memset(M, 0, sizeof(M)); + return HAL_ERROR_IMPOSSIBLE; + } + + point_double(M[0], M[1], curve); + + /* + * Walk down bits of the scalar, performing dummy operations to mask + * timing while hunting for the most significant bit of the scalar. + * + * Note that, in order for this timing protection to work, the + * number of iterations in the loop has to depend on the order of + * the base point rather than on the scalar. + */ + + int dummy_mode = 1; + + for (int bit_index = fp_count_bits(unconst_fp_int(curve->n)) - 1; bit_index >= 0; bit_index--) { + + const int digit_index = bit_index / DIGIT_BIT; + const fp_digit digit = digit_index < k->used ? k->dp[digit_index] : 0; + const fp_digit mask = ((fp_digit) 1) << (bit_index % DIGIT_BIT); + const int bit = (digit & mask) != 0; + + if (dummy_mode) { + point_add (M[0], M[1], M[2], curve); + point_double (M[1], M[2], curve); + dummy_mode = !bit; /* Dummy until we find MSB */ + } + + else { + point_add (M[0], M[1], M[bit^1], curve); + point_double (M[bit], M[bit], curve); + } + } + + /* + * Copy result out, map back to affine if requested, then done. + */ + + point_copy(M[0], R); + hal_error_t err = map ? point_to_affine(R, curve) : HAL_OK; + memset(M, 0, sizeof(M)); + return err; +} + +/* + * Testing only: ECDSA key generation and signature both have a + * critical dependency on random numbers, but we can't use the random + * number generator when testing against static test vectors. So add a + * wrapper around the random number generator calls, with a hook to + * let us override the generator for test purposes. Do NOT use this + * in production, kids. + */ + +#if HAL_ECDSA_DEBUG_ONLY_STATIC_TEST_VECTOR_RANDOM + +#warning hal_ecdsa random number generator overridden for test purposes +#warning DO NOT USE THIS IN PRODUCTION + +typedef hal_error_t (*rng_override_test_function_t)(void *, const size_t); + +static rng_override_test_function_t rng_test_override_function = 0; + +rng_override_test_function_t hal_ecdsa_set_rng_override_test_function(rng_override_test_function_t new_func) +{ + rng_override_test_function_t old_func = rng_test_override_function; + rng_test_override_function = new_func; + return old_func; +} + +static inline hal_error_t get_random(void *buffer, const size_t length) +{ + if (rng_test_override_function) + return rng_test_override_function(buffer, length); + else + return hal_get_random(buffer, length); +} + +#else /* HAL_ECDSA_DEBUG_ONLY_STATIC_TEST_VECTOR_RANDOM */ + +static inline hal_error_t get_random(void *buffer, const size_t length) +{ + return hal_get_random(buffer, length); +} + +#endif /* HAL_ECDSA_DEBUG_ONLY_STATIC_TEST_VECTOR_RANDOM */ + +/* + * Pick a random point on the curve, return random scalar and + * resulting point. + */ + +static hal_error_t point_pick_random(const ecdsa_curve_t * const curve, + fp_int *k, + ec_point_t *P) +{ + hal_error_t err; + + assert(curve != NULL && k != NULL && P != NULL); + + /* + * Pick a random scalar corresponding to a point on the curve. Per + * the NSA (gulp) Suite B guidelines, we ask the CSPRNG for 64 more + * bits than we need, which should be enough to mask any bias + * induced by the modular reduction. + * + * We're picking a point out of the subgroup generated by the base + * point on the elliptic curve, so the modulus for this calculation + * is the order of the base point. + * + * Zero is an excluded value, but the chance of a non-broken CSPRNG + * returning zero is so low that it would almost certainly indicate + * an undiagnosed bug in the CSPRNG. + */ + + uint8_t k_buf[fp_unsigned_bin_size(unconst_fp_int(curve->n)) + 8]; + + do { + + if ((err = get_random(k_buf, sizeof(k_buf))) != HAL_OK) + return err; + + fp_read_unsigned_bin(k, k_buf, sizeof(k_buf)); + + if (fp_iszero(k) || fp_mod(k, unconst_fp_int(curve->n), k) != FP_OKAY) + return HAL_ERROR_IMPOSSIBLE; + + } while (fp_iszero(k)); + + memset(k_buf, 0, sizeof(k_buf)); + + /* + * Calculate P = kG and return. + */ + + fp_copy(curve->Gx, P->x); + fp_copy(curve->Gy, P->y); + fp_set(P->z, 1); + + return point_scalar_multiply(k, P, P, curve, 1); +} + +/* + * Test whether a point really is on a particular curve. This is + * called "validation" when applied to a public key, and is required + * before verifying a signature. + */ + +static int point_is_on_curve(const ec_point_t * const P, + const ecdsa_curve_t * const curve) +{ + assert(P != NULL && curve != NULL); + + fp_int t1[1]; fp_init(t1); + fp_int t2[1]; fp_init(t2); + + /* + * Compute y**2 - x**3 + 3*x. + */ + + fp_sqr(unconst_fp_int(P->y), t1); /* t1 = y**2 */ + fp_sqr(unconst_fp_int(P->x), t2); /* t2 = x**2 */ + if (fp_mod(t2, unconst_fp_int(curve->q), t2) != FP_OKAY) + return 0; + fp_mul(unconst_fp_int(P->x), t2, t2); /* t2 = x**3 */ + fp_sub(t1, t2, t1); /* t1 = y**2 - x**3 */ + fp_add(t1, unconst_fp_int(P->x), t1); /* t1 = y**2 - x**3 + 1*x */ + fp_add(t1, unconst_fp_int(P->x), t1); /* t1 = y**2 - x**3 + 2*x */ + fp_add(t1, unconst_fp_int(P->x), t1); /* t1 = y**2 - x**3 + 3*x */ + + /* + * Normalize and test whether computed value matches b. + */ + + if (fp_mod(t1, unconst_fp_int(curve->q), t1) != FP_OKAY) + return 0; + while (fp_cmp_d(t1, 0) == FP_LT) + fp_add(t1, unconst_fp_int(curve->q), t1); + while (fp_cmp(t1, unconst_fp_int(curve->q)) != FP_LT) + fp_sub(t1, unconst_fp_int(curve->q), t1); + + return fp_cmp(t1, unconst_fp_int(curve->b)) == FP_EQ; +} + +/* + * Generate a new ECDSA key. + */ + +hal_error_t hal_ecdsa_key_gen(hal_ecdsa_key_t **key_, + void *keybuf, const size_t keybuf_len, + const hal_ecdsa_curve_t curve_) +{ + const ecdsa_curve_t * const curve = get_curve(curve_); + hal_ecdsa_key_t *key = keybuf; + hal_error_t err; + + if (key_ == NULL || key == NULL || keybuf_len < sizeof(*key) || curve == NULL) + return HAL_ERROR_BAD_ARGUMENTS; + + memset(keybuf, 0, keybuf_len); + + key->type = HAL_ECDSA_PRIVATE; + key->curve = curve_; + + if ((err = point_pick_random(curve, key->d, key->Q)) != HAL_OK) + return err; + + assert(point_is_on_curve(key->Q, curve)); + + *key_ = key; + return HAL_OK; +} + +/* + * Extract key type (public or private). + */ + +hal_error_t hal_ecdsa_key_get_type(const hal_ecdsa_key_t * const key, + hal_ecdsa_key_type_t *key_type) +{ + if (key == NULL || key_type == NULL) + return HAL_ERROR_BAD_ARGUMENTS; + + *key_type = key->type; + return HAL_OK; +} + +/* + * Extract name of curve underlying a key. + */ + +hal_error_t hal_ecdsa_key_get_curve(const hal_ecdsa_key_t * const key, + hal_ecdsa_curve_t *curve) +{ + if (key == NULL || curve == NULL) + return HAL_ERROR_BAD_ARGUMENTS; + + *curve = key->curve; + return HAL_OK; +} + +/* + * Extract public key components. + */ + +hal_error_t hal_ecdsa_key_get_public(const hal_ecdsa_key_t * const key, + uint8_t *x, size_t *x_len, const size_t x_max, + uint8_t *y, size_t *y_len, const size_t y_max) +{ + if (key == NULL || (x_len == NULL && x != NULL) || (y_len == NULL && y != NULL)) + return HAL_ERROR_BAD_ARGUMENTS; + + if (x_len != NULL) + *x_len = fp_unsigned_bin_size(unconst_fp_int(key->Q->x)); + + if (y_len != NULL) + *y_len = fp_unsigned_bin_size(unconst_fp_int(key->Q->y)); + + if ((x != NULL && *x_len > x_max) || + (y != NULL && *y_len > y_max)) + return HAL_ERROR_RESULT_TOO_LONG; + + if (x != NULL) + fp_to_unsigned_bin(unconst_fp_int(key->Q->x), x); + + if (y != NULL) + fp_to_unsigned_bin(unconst_fp_int(key->Q->y), y); + + return HAL_OK; +} + +/* + * Clear a key. + */ + +void hal_ecdsa_key_clear(hal_ecdsa_key_t *key) +{ + if (key != NULL) + memset(key, 0, sizeof(*key)); +} + +/* + * Load a public key from components, and validate that the public key + * really is on the named curve. + */ + +hal_error_t hal_ecdsa_key_load_public(hal_ecdsa_key_t **key_, + void *keybuf, const size_t keybuf_len, + const hal_ecdsa_curve_t curve_, + const uint8_t * const x, const size_t x_len, + const uint8_t * const y, const size_t y_len) +{ + const ecdsa_curve_t * const curve = get_curve(curve_); + hal_ecdsa_key_t *key = keybuf; + + if (key_ == NULL || key == NULL || keybuf_len < sizeof(*key) || curve == NULL || x == NULL || y == NULL) + return HAL_ERROR_BAD_ARGUMENTS; + + memset(keybuf, 0, keybuf_len); + + key->type = HAL_ECDSA_PUBLIC; + key->curve = curve_; + + fp_read_unsigned_bin(key->Q->x, unconst_uint8_t(x), x_len); + fp_read_unsigned_bin(key->Q->y, unconst_uint8_t(y), y_len); + fp_set(key->Q->z, 1); + + if (!point_is_on_curve(key->Q, curve)) + return HAL_ERROR_KEY_NOT_ON_CURVE; + + *key_ = key; + + return HAL_OK; +} + +/* + * Load a private key from components; does all the same things as + * hal_ecdsa_key_load_public(), then loads the private key itself and + * adjusts the key type. + * + * For extra paranoia, we could check Q == dG. + */ + +hal_error_t hal_ecdsa_key_load_private(hal_ecdsa_key_t **key_, + void *keybuf, const size_t keybuf_len, + const hal_ecdsa_curve_t curve_, + const uint8_t * const x, const size_t x_len, + const uint8_t * const y, const size_t y_len, + const uint8_t * const d, const size_t d_len) +{ + hal_ecdsa_key_t *key = keybuf; + hal_error_t err; + + if (d == NULL) + return HAL_ERROR_BAD_ARGUMENTS; + + if ((err = hal_ecdsa_key_load_public(key_, keybuf, keybuf_len, curve_, x, x_len, y, y_len)) != HAL_OK) + return err; + + key->type = HAL_ECDSA_PRIVATE; + fp_read_unsigned_bin(key->d, unconst_uint8_t(d), d_len); + return HAL_OK; +} + +/* + * Write public key in X9.62 ECPoint format (ASN.1 OCTET STRING, first octet is compression flag). + */ + +hal_error_t hal_ecdsa_key_to_ecpoint(const hal_ecdsa_key_t * const key, + uint8_t *der, size_t *der_len, const size_t der_max) +{ + if (key == NULL) + return HAL_ERROR_BAD_ARGUMENTS; + + const ecdsa_curve_t * const curve = get_curve(key->curve); + if (curve == NULL) + return HAL_ERROR_IMPOSSIBLE; + + const size_t q_len = fp_unsigned_bin_size(unconst_fp_int(curve->q)); + const size_t Qx_len = fp_unsigned_bin_size(unconst_fp_int(key->Q->x)); + const size_t Qy_len = fp_unsigned_bin_size(unconst_fp_int(key->Q->y)); + assert(q_len >= Qx_len && q_len >= Qy_len); + + const size_t vlen = q_len * 2 + 1; + size_t hlen; + + hal_error_t err = hal_asn1_encode_header(ASN1_OCTET_STRING, vlen, der, &hlen, der_max); + + if (der_len != NULL) + *der_len = hlen + vlen; + + if (der == NULL || err != HAL_OK) + return err; + + assert(hlen + vlen <= der_max); + + uint8_t *d = der + hlen; + memset(d, 0, vlen); + + *d++ = 0x04; /* uncompressed */ + + fp_to_unsigned_bin(unconst_fp_int(key->Q->x), d + q_len - Qx_len); + d += q_len; + + fp_to_unsigned_bin(unconst_fp_int(key->Q->y), d + q_len - Qy_len); + d += q_len; + + assert(d <= der + der_max); + + return HAL_OK; +} + +/* + * Convenience wrapper to return how many bytes a key would take if + * encoded as an ECPoint. + */ + +size_t hal_ecdsa_key_to_ecpoint_len(const hal_ecdsa_key_t * const key) +{ + size_t len; + return hal_ecdsa_key_to_ecpoint(key, NULL, &len, 0) == HAL_OK ? len : 0; +} + +/* + * Read public key in X9.62 ECPoint format (ASN.1 OCTET STRING, first octet is compression flag). + * ECPoint format doesn't include a curve identifier, so caller has to supply one. + */ + +hal_error_t hal_ecdsa_key_from_ecpoint(hal_ecdsa_key_t **key_, + void *keybuf, const size_t keybuf_len, + const uint8_t * const der, const size_t der_len, + const hal_ecdsa_curve_t curve) +{ + hal_ecdsa_key_t *key = keybuf; + + if (key_ == NULL || key == NULL || keybuf_len < sizeof(*key) || get_curve(curve) == NULL) + return HAL_ERROR_BAD_ARGUMENTS; + + memset(keybuf, 0, keybuf_len); + key->type = HAL_ECDSA_PUBLIC; + key->curve = curve; + + size_t hlen, vlen; + hal_error_t err; + + if ((err = hal_asn1_decode_header(ASN1_OCTET_STRING, der, der_len, &hlen, &vlen)) != HAL_OK) + return err; + + const uint8_t * const der_end = der + hlen + vlen; + const uint8_t *d = der + hlen; + + if (vlen < 3 || (vlen & 1) == 0 || *d++ != 0x04) + lose(HAL_ERROR_ASN1_PARSE_FAILED); + + vlen = vlen/2 - 1; + + fp_read_unsigned_bin(key->Q->x, unconst_uint8_t(d), vlen); + d += vlen; + + fp_read_unsigned_bin(key->Q->y, unconst_uint8_t(d), vlen); + d += vlen; + + fp_set(key->Q->z, 1); + + if (d != der_end) + lose(HAL_ERROR_ASN1_PARSE_FAILED); + + *key_ = key; + return HAL_OK; + + fail: + memset(keybuf, 0, keybuf_len); + return err; +} + +/* + * Write private key in RFC 5915 ASN.1 DER format. + * + * This is hand-coded, and is approaching the limit where one should + * probably be using an ASN.1 compiler like asn1c instead. + */ + +hal_error_t hal_ecdsa_key_to_der(const hal_ecdsa_key_t * const key, + uint8_t *der, size_t *der_len, const size_t der_max) +{ + if (key == NULL || key->type != HAL_ECDSA_PRIVATE) + return HAL_ERROR_BAD_ARGUMENTS; + + const ecdsa_curve_t * const curve = get_curve(key->curve); + if (curve == NULL) + return HAL_ERROR_IMPOSSIBLE; + + const size_t q_len = fp_unsigned_bin_size(unconst_fp_int(curve->q)); + const size_t d_len = fp_unsigned_bin_size(unconst_fp_int(key->d)); + const size_t Qx_len = fp_unsigned_bin_size(unconst_fp_int(key->Q->x)); + const size_t Qy_len = fp_unsigned_bin_size(unconst_fp_int(key->Q->y)); + assert(q_len >= d_len && q_len >= Qx_len && q_len >= Qy_len); + + fp_int version[1]; + fp_set(version, 1); + + hal_error_t err; + + size_t version_len, hlen, hlen_oct, hlen_oid, hlen_exp0, hlen_bit, hlen_exp1; + + if ((err = hal_asn1_encode_integer(version, NULL, &version_len, 0)) != HAL_OK || + (err = hal_asn1_encode_header(ASN1_OCTET_STRING, q_len, NULL, &hlen_oct, 0)) != HAL_OK || + (err = hal_asn1_encode_header(ASN1_OBJECT_IDENTIFIER, curve->oid_len, NULL, &hlen_oid, 0)) != HAL_OK || + (err = hal_asn1_encode_header(ASN1_EXPLICIT_0, hlen_oid + curve->oid_len, NULL, &hlen_exp0, 0)) != HAL_OK || + (err = hal_asn1_encode_header(ASN1_BIT_STRING, (q_len + 1) * 2, NULL, &hlen_bit, 0)) != HAL_OK || + (err = hal_asn1_encode_header(ASN1_EXPLICIT_1, hlen_bit + (q_len + 1) * 2, NULL, &hlen_exp1, 0)) != HAL_OK) + return err; + + const size_t vlen = (version_len + + hlen_oct + q_len + + hlen_oid + hlen_exp0 + curve->oid_len + + hlen_bit + hlen_exp1 + (q_len + 1) * 2); + + err = hal_asn1_encode_header(ASN1_SEQUENCE, vlen, der, &hlen, der_max); + + if (der_len != NULL) + *der_len = hlen + vlen; + + if (der == NULL || err != HAL_OK) + return err; + + uint8_t *d = der + hlen; + memset(d, 0, vlen); + + if ((err = hal_asn1_encode_integer(version, d, NULL, der + der_max - d)) != HAL_OK) + return err; + d += version_len; + + if ((err = hal_asn1_encode_header(ASN1_OCTET_STRING, q_len, d, &hlen, der + der_max - d)) != HAL_OK) + return err; + d += hlen; + fp_to_unsigned_bin(unconst_fp_int(key->d), d + q_len - d_len); + d += q_len; + + if ((err = hal_asn1_encode_header(ASN1_EXPLICIT_0, hlen_oid + curve->oid_len, d, &hlen, der + der_max - d)) != HAL_OK) + return err; + d += hlen; + if ((err = hal_asn1_encode_header(ASN1_OBJECT_IDENTIFIER, curve->oid_len, d, &hlen, der + der_max - d)) != HAL_OK) + return err; + d += hlen; + memcpy(d, curve->oid, curve->oid_len); + d += curve->oid_len; + + if ((err = hal_asn1_encode_header(ASN1_EXPLICIT_1, hlen_bit + (q_len + 1) * 2, d, &hlen, der + der_max - d)) != HAL_OK) + return err; + d += hlen; + if ((err = hal_asn1_encode_header(ASN1_BIT_STRING, (q_len + 1) * 2, d, &hlen, der + der_max - d)) != HAL_OK) + return err; + d += hlen; + *d++ = 0x00; + *d++ = 0x04; + fp_to_unsigned_bin(unconst_fp_int(key->Q->x), d + q_len - Qx_len); + d += q_len; + fp_to_unsigned_bin(unconst_fp_int(key->Q->y), d + q_len - Qy_len); + d += q_len; + + assert(d == der + der_max); + + return HAL_OK; +} + +/* + * Convenience wrapper to return how many bytes a private key would + * take if encoded as DER. + */ + +size_t hal_ecdsa_key_to_der_len(const hal_ecdsa_key_t * const key) +{ + size_t len; + return hal_ecdsa_key_to_der(key, NULL, &len, 0) == HAL_OK ? len : 0; +} + +/* + * Read private key in RFC 5915 ASN.1 DER format. + * + * This is hand-coded, and is approaching the limit where one should + * probably be using an ASN.1 compiler like asn1c instead. + */ + +hal_error_t hal_ecdsa_key_from_der(hal_ecdsa_key_t **key_, + void *keybuf, const size_t keybuf_len, + const uint8_t * const der, const size_t der_len) +{ + hal_ecdsa_key_t *key = keybuf; + + if (key_ == NULL || key == NULL || keybuf_len < sizeof(*key)) + return HAL_ERROR_BAD_ARGUMENTS; + + memset(keybuf, 0, keybuf_len); + key->type = HAL_ECDSA_PRIVATE; + + size_t hlen, vlen; + hal_error_t err; + + if ((err = hal_asn1_decode_header(ASN1_SEQUENCE, der, der_len, &hlen, &vlen)) != HAL_OK) + return err; + + const uint8_t * const der_end = der + hlen + vlen; + const uint8_t *d = der + hlen; + const ecdsa_curve_t *curve = NULL; + fp_int version[1]; + + if ((err = hal_asn1_decode_integer(version, d, &hlen, vlen)) != HAL_OK) + goto fail; + if (fp_cmp_d(version, 1) != FP_EQ) + lose(HAL_ERROR_ASN1_PARSE_FAILED); + d += hlen; + + if ((err = hal_asn1_decode_header(ASN1_OCTET_STRING, d, der_end - d, &hlen, &vlen)) != HAL_OK) + return err; + d += hlen; + fp_read_unsigned_bin(key->d, unconst_uint8_t(d), vlen); + d += vlen; + + if ((err = hal_asn1_decode_header(ASN1_EXPLICIT_0, d, der_end - d, &hlen, &vlen)) != HAL_OK) + return err; + d += hlen; + if (vlen > der_end - d) + lose(HAL_ERROR_ASN1_PARSE_FAILED); + if ((err = hal_asn1_decode_header(ASN1_OBJECT_IDENTIFIER, d, vlen, &hlen, &vlen)) != HAL_OK) + return err; + d += hlen; + for (key->curve = (hal_ecdsa_curve_t) 0; (curve = get_curve(key->curve)) != NULL; key->curve++) + if (vlen == curve->oid_len && memcmp(d, curve->oid, vlen) == 0) + break; + if (curve == NULL) + lose(HAL_ERROR_ASN1_PARSE_FAILED); + d += vlen; + + if ((err = hal_asn1_decode_header(ASN1_EXPLICIT_1, d, der_end - d, &hlen, &vlen)) != HAL_OK) + return err; + d += hlen; + if (vlen > der_end - d) + lose(HAL_ERROR_ASN1_PARSE_FAILED); + if ((err = hal_asn1_decode_header(ASN1_BIT_STRING, d, vlen, &hlen, &vlen)) != HAL_OK) + return err; + d += hlen; + if (vlen < 4 || (vlen & 1) != 0 || *d++ != 0x00 || *d++ != 0x04) + lose(HAL_ERROR_ASN1_PARSE_FAILED); + vlen = vlen/2 - 1; + fp_read_unsigned_bin(key->Q->x, unconst_uint8_t(d), vlen); + d += vlen; + fp_read_unsigned_bin(key->Q->y, unconst_uint8_t(d), vlen); + d += vlen; + fp_set(key->Q->z, 1); + + if (d != der_end) + lose(HAL_ERROR_ASN1_PARSE_FAILED); + + *key_ = key; + return HAL_OK; + + fail: + memset(keybuf, 0, keybuf_len); + return err; +} + +/* + * Encode a signature in PKCS #11 format: an octet string consisting + * of concatenated values for r and s, each padded (if necessary) out + * to the byte length of the order of the base point. + */ + +static hal_error_t encode_signature_pkcs11(const ecdsa_curve_t * const curve, + const fp_int * const r, const fp_int * const s, + uint8_t *signature, size_t *signature_len, const size_t signature_max) +{ + assert(curve != NULL && r != NULL && s != NULL); + + const size_t n_len = fp_unsigned_bin_size(unconst_fp_int(curve->n)); + const size_t r_len = fp_unsigned_bin_size(unconst_fp_int(r)); + const size_t s_len = fp_unsigned_bin_size(unconst_fp_int(s)); + + if (n_len < r_len || n_len < s_len) + return HAL_ERROR_IMPOSSIBLE; + + if (signature_len != NULL) + *signature_len = n_len * 2; + + if (signature == NULL) + return HAL_OK; + + if (signature_max < n_len * 2) + return HAL_ERROR_RESULT_TOO_LONG; + + memset(signature, 0, n_len * 2); + fp_to_unsigned_bin(unconst_fp_int(r), signature + 1 * n_len - r_len); + fp_to_unsigned_bin(unconst_fp_int(s), signature + 2 * n_len - s_len); + + return HAL_OK; +} + +/* + * Decode a signature from PKCS #11 format: an octet string consisting + * of concatenated values for r and s, each of which occupies half of + * the octet string (which must therefore be of even length). + */ + +static hal_error_t decode_signature_pkcs11(const ecdsa_curve_t * const curve, + fp_int *r, fp_int *s, + const uint8_t * const signature, const size_t signature_len) +{ + assert(curve != NULL && r != NULL && s != NULL); + + if (signature == NULL || (signature_len & 1) != 0) + return HAL_ERROR_BAD_ARGUMENTS; + + const size_t n_len = signature_len / 2; + + if (n_len > fp_unsigned_bin_size(unconst_fp_int(curve->n))) + return HAL_ERROR_BAD_ARGUMENTS; + + fp_read_unsigned_bin(r, unconst_uint8_t(signature) + 0 * n_len, n_len); + fp_read_unsigned_bin(s, unconst_uint8_t(signature) + 1 * n_len, n_len); + + return HAL_OK; +} + +/* + * Encode a signature in ASN.1 format SEQUENCE { INTEGER r, INTEGER s }. + */ + +static hal_error_t encode_signature_asn1(const ecdsa_curve_t * const curve, + const fp_int * const r, const fp_int * const s, + uint8_t *signature, size_t *signature_len, const size_t signature_max) +{ + assert(curve != NULL && r != NULL && s != NULL); + + size_t hlen, r_len, s_len; + hal_error_t err; + + if ((err = hal_asn1_encode_integer(r, NULL, &r_len, 0)) != HAL_OK || + (err = hal_asn1_encode_integer(s, NULL, &s_len, 0)) != HAL_OK) + return err; + + const size_t vlen = r_len + s_len; + + err = hal_asn1_encode_header(ASN1_SEQUENCE, vlen, signature, &hlen, signature_max); + + if (signature_len != NULL) + *signature_len = hlen + vlen; + + if (signature == NULL || err != HAL_OK) + return err; + + uint8_t * const r_out = signature + hlen; + uint8_t * const s_out = r_out + r_len; + + if ((err = hal_asn1_encode_integer(r, r_out, NULL, signature_max - (r_out - signature))) != HAL_OK || + (err = hal_asn1_encode_integer(s, s_out, NULL, signature_max - (s_out - signature))) != HAL_OK) + return err; + + return HAL_OK; +} + +/* + * Decode a signature from ASN.1 format SEQUENCE { INTEGER r, INTEGER s }. + */ + +static hal_error_t decode_signature_asn1(const ecdsa_curve_t * const curve, + fp_int *r, fp_int *s, + const uint8_t * const signature, const size_t signature_len) +{ + assert(curve != NULL && r != NULL && s != NULL); + + if (signature == NULL) + return HAL_ERROR_BAD_ARGUMENTS; + + size_t len1, len2; + hal_error_t err; + + if ((err = hal_asn1_decode_header(ASN1_SEQUENCE, signature, signature_len, &len1, &len2)) != HAL_OK) + return err; + + const uint8_t * der = signature + len1; + const uint8_t * const der_end = der + len2; + + if ((err = hal_asn1_decode_integer(r, der, &len1, der_end - der)) != HAL_OK) + return err; + der += len1; + + if ((err = hal_asn1_decode_integer(s, der, &len1, der_end - der)) != HAL_OK) + return err; + der += len1; + + if (der != der_end) + return HAL_ERROR_ASN1_PARSE_FAILED; + + return HAL_OK; +} + +/* + * Sign a caller-supplied hash. + */ + +hal_error_t hal_ecdsa_sign(const hal_ecdsa_key_t * const key, + const uint8_t * const hash, const size_t hash_len, + uint8_t *signature, size_t *signature_len, const size_t signature_max, + const hal_ecdsa_signature_format_t signature_format) +{ + if (key == NULL || hash == NULL || signature == NULL || signature_len == NULL || key->type != HAL_ECDSA_PRIVATE) + return HAL_ERROR_BAD_ARGUMENTS; + + const ecdsa_curve_t * const curve = get_curve(key->curve); + if (curve == NULL) + return HAL_ERROR_IMPOSSIBLE; + + fp_int k[1]; fp_init(k); + fp_int r[1]; fp_init(r); + fp_int s[1]; fp_init(s); + fp_int e[1]; fp_init(e); + + fp_int * const n = unconst_fp_int(curve->n); + fp_int * const d = unconst_fp_int(key->d); + + ec_point_t R[1]; + memset(R, 0, sizeof(R)); + + hal_error_t err; + + fp_read_unsigned_bin(e, unconst_uint8_t(hash), hash_len); + + do { + + /* + * Pick random curve point R, then calculate r = Rx % n. + * If r == 0, we can't use this point, so go try again. + */ + + if ((err = point_pick_random(curve, k, R)) != HAL_OK) + goto fail; + + assert(point_is_on_curve(R, curve)); + + if (fp_mod(R->x, n, r) != FP_OKAY) + lose(HAL_ERROR_IMPOSSIBLE); + + if (fp_iszero(r)) + continue; + + /* + * Calculate s = ((e + dr)/k) % n. + * If s == 0, we can't use this point, so go try again. + */ + + if (fp_mulmod (d, r, n, s) != FP_OKAY) + lose(HAL_ERROR_IMPOSSIBLE); + + fp_add (e, s, s); + + if (fp_mod (s, n, s) != FP_OKAY || + fp_invmod (k, n, k) != FP_OKAY || + fp_mulmod (s, k, n, s) != FP_OKAY) + lose(HAL_ERROR_IMPOSSIBLE); + + } while (fp_iszero(s)); + + /* + * Encode the signature, then we're done. + */ + + switch (signature_format) { + + case HAL_ECDSA_SIGNATURE_FORMAT_ASN1: + if ((err = encode_signature_asn1(curve, r, s, signature, signature_len, signature_max)) != HAL_OK) + goto fail; + break; + + case HAL_ECDSA_SIGNATURE_FORMAT_PKCS11: + if ((err = encode_signature_pkcs11(curve, r, s, signature, signature_len, signature_max)) != HAL_OK) + goto fail; + break; + + default: + lose(HAL_ERROR_BAD_ARGUMENTS); + } + + err = HAL_OK; + + fail: + fp_zero(k); fp_zero(r); fp_zero(s); fp_zero(e); + memset(R, 0, sizeof(R)); + return err; +} + +/* + * Verify a signature using a caller-supplied hash. + */ + +hal_error_t hal_ecdsa_verify(const hal_ecdsa_key_t * const key, + const uint8_t * const hash, const size_t hash_len, + const uint8_t * const signature, const size_t signature_len, + const hal_ecdsa_signature_format_t signature_format) +{ + assert(key != NULL && hash != NULL && signature != NULL); + + const ecdsa_curve_t * const curve = get_curve(key->curve); + + if (curve == NULL) + return HAL_ERROR_IMPOSSIBLE; + + if (!point_is_on_curve(key->Q, curve)) + return HAL_ERROR_KEY_NOT_ON_CURVE; + + fp_int * const n = unconst_fp_int(curve->n); + + hal_error_t err; + fp_int r[1], s[1], e[1], w[1], u1[1], u2[1], v[1]; + ec_point_t u1G[1], u2Q[1], R[1]; + + fp_init(w); fp_init(u1); fp_init(u2); fp_init(v); + memset(u1G, 0, sizeof(u1G)); + memset(u2Q, 0, sizeof(u2Q)); + memset(R, 0, sizeof(R)); + + /* + * Start by decoding the signature. + */ + + switch (signature_format) { + + case HAL_ECDSA_SIGNATURE_FORMAT_ASN1: + if ((err = decode_signature_asn1(curve, r, s, signature, signature_len)) != HAL_OK) + return err; + break; + + case HAL_ECDSA_SIGNATURE_FORMAT_PKCS11: + if ((err = decode_signature_pkcs11(curve, r, s, signature, signature_len)) != HAL_OK) + return err; + break; + + default: + return HAL_ERROR_BAD_ARGUMENTS; + } + + /* + * Check that r and s are in the allowed range, read the hash, then + * compute: + * + * w = 1 / s + * u1 = e * w + * u2 = r * w + * R = u1 * G + u2 * Q. + */ + + if (fp_cmp_d(r, 1) == FP_LT || fp_cmp(r, n) != FP_LT || + fp_cmp_d(s, 1) == FP_LT || fp_cmp(s, n) != FP_LT) + return HAL_ERROR_INVALID_SIGNATURE; + + fp_read_unsigned_bin(e, unconst_uint8_t(hash), hash_len); + + if (fp_invmod(s, n, w) != FP_OKAY || + fp_mulmod(e, w, n, u1) != FP_OKAY || + fp_mulmod(r, w, n, u2) != FP_OKAY) + return HAL_ERROR_IMPOSSIBLE; + + fp_copy(unconst_fp_int(curve->Gx), u1G->x); + fp_copy(unconst_fp_int(curve->Gy), u1G->y); + fp_set(u1G->z, 1); + + if ((err = point_scalar_multiply(u1, u1G, u1G, curve, 0)) != HAL_OK || + (err = point_scalar_multiply(u2, key->Q, u2Q, curve, 0)) != HAL_OK) + return err; + + if (point_is_infinite(u1G)) + point_copy(u2Q, R); + else if (point_is_infinite(u2Q)) + point_copy(u1G, R); + else + point_add(u1G, u2Q, R, curve); + + /* + * Signature is OK if + * R is not the point at infinity, and + * Rx is congruent to r mod n. + */ + + if (point_is_infinite(R)) + return HAL_ERROR_INVALID_SIGNATURE; + + if ((err = point_to_affine(R, curve)) != HAL_OK) + return err; + + if (fp_mod(R->x, n, v) != FP_OKAY) + return HAL_ERROR_IMPOSSIBLE; + + return fp_cmp(v, r) == FP_EQ ? HAL_OK : HAL_ERROR_INVALID_SIGNATURE; +} + +/* + * Local variables: + * indent-tabs-mode: nil + * End: + */ diff --git a/ecdsa_curves.h b/ecdsa_curves.h new file mode 100644 index 0000000..80896ec --- /dev/null +++ b/ecdsa_curves.h @@ -0,0 +1,92 @@ +/* + * ecdsa_curves.h + * -------------- + * Curve parameters for NIST P-256, P-384, and P-521 curves. + * + * At some point we may want to replace this file with one generated + * by a Python script, to make it easier to check the curve parameters. + * + * Authors: Rob Austein + * Copyright (c) 2015, SUNET + * + * 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 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 OWNER 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. + */ + +static const uint8_t p256_q[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; +static const uint8_t p256_b[] = { 0x5A, 0xC6, 0x35, 0xD8, 0xAA, 0x3A, 0x93, 0xE7, 0xB3, 0xEB, 0xBD, 0x55, 0x76, 0x98, 0x86, 0xBC, + 0x65, 0x1D, 0x06, 0xB0, 0xCC, 0x53, 0xB0, 0xF6, 0x3B, 0xCE, 0x3C, 0x3E, 0x27, 0xD2, 0x60, 0x4B }; +static const uint8_t p256_Gx[] = { 0x6B, 0x17, 0xD1, 0xF2, 0xE1, 0x2C, 0x42, 0x47, 0xF8, 0xBC, 0xE6, 0xE5, 0x63, 0xA4, 0x40, 0xF2, + 0x77, 0x03, 0x7D, 0x81, 0x2D, 0xEB, 0x33, 0xA0, 0xF4, 0xA1, 0x39, 0x45, 0xD8, 0x98, 0xC2, 0x96 }; +static const uint8_t p256_Gy[] = { 0x4F, 0xE3, 0x42, 0xE2, 0xFE, 0x1A, 0x7F, 0x9B, 0x8E, 0xE7, 0xEB, 0x4A, 0x7C, 0x0F, 0x9E, 0x16, + 0x2B, 0xCE, 0x33, 0x57, 0x6B, 0x31, 0x5E, 0xCE, 0xCB, 0xB6, 0x40, 0x68, 0x37, 0xBF, 0x51, 0xF5 }; +static const uint8_t p256_n[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xBC, 0xE6, 0xFA, 0xAD, 0xA7, 0x17, 0x9E, 0x84, 0xF3, 0xB9, 0xCA, 0xC2, 0xFC, 0x63, 0x25, 0x51 }; +static const uint8_t p256_oid[] = { 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07 }; + +static const uint8_t p384_q[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, + 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF }; +static const uint8_t p384_b[] = { 0xB3, 0x31, 0x2F, 0xA7, 0xE2, 0x3E, 0xE7, 0xE4, 0x98, 0x8E, 0x05, 0x6B, 0xE3, 0xF8, 0x2D, 0x19, + 0x18, 0x1D, 0x9C, 0x6E, 0xFE, 0x81, 0x41, 0x12, 0x03, 0x14, 0x08, 0x8F, 0x50, 0x13, 0x87, 0x5A, + 0xC6, 0x56, 0x39, 0x8D, 0x8A, 0x2E, 0xD1, 0x9D, 0x2A, 0x85, 0xC8, 0xED, 0xD3, 0xEC, 0x2A, 0xEF }; +static const uint8_t p384_Gx[] = { 0xAA, 0x87, 0xCA, 0x22, 0xBE, 0x8B, 0x05, 0x37, 0x8E, 0xB1, 0xC7, 0x1E, 0xF3, 0x20, 0xAD, 0x74, + 0x6E, 0x1D, 0x3B, 0x62, 0x8B, 0xA7, 0x9B, 0x98, 0x59, 0xF7, 0x41, 0xE0, 0x82, 0x54, 0x2A, 0x38, + 0x55, 0x02, 0xF2, 0x5D, 0xBF, 0x55, 0x29, 0x6C, 0x3A, 0x54, 0x5E, 0x38, 0x72, 0x76, 0x0A, 0xB7 }; +static const uint8_t p384_Gy[] = { 0x36, 0x17, 0xDE, 0x4A, 0x96, 0x26, 0x2C, 0x6F, 0x5D, 0x9E, 0x98, 0xBF, 0x92, 0x92, 0xDC, 0x29, + 0xF8, 0xF4, 0x1D, 0xBD, 0x28, 0x9A, 0x14, 0x7C, 0xE9, 0xDA, 0x31, 0x13, 0xB5, 0xF0, 0xB8, 0xC0, + 0x0A, 0x60, 0xB1, 0xCE, 0x1D, 0x7E, 0x81, 0x9D, 0x7A, 0x43, 0x1D, 0x7C, 0x90, 0xEA, 0x0E, 0x5F }; +static const uint8_t p384_n[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC7, 0x63, 0x4D, 0x81, 0xF4, 0x37, 0x2D, 0xDF, + 0x58, 0x1A, 0x0D, 0xB2, 0x48, 0xB0, 0xA7, 0x7A, 0xEC, 0xEC, 0x19, 0x6A, 0xCC, 0xC5, 0x29, 0x73 }; +static const uint8_t p384_oid[] = { 0x2b, 0x81, 0x04, 0x00, 0x22 }; + +static const uint8_t p521_q[] = { 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF }; +static const uint8_t p521_b[] = { 0x00, 0x51, 0x95, 0x3E, 0xB9, 0x61, 0x8E, 0x1C, 0x9A, 0x1F, 0x92, 0x9A, 0x21, 0xA0, 0xB6, 0x85, + 0x40, 0xEE, 0xA2, 0xDA, 0x72, 0x5B, 0x99, 0xB3, 0x15, 0xF3, 0xB8, 0xB4, 0x89, 0x91, 0x8E, 0xF1, + 0x09, 0xE1, 0x56, 0x19, 0x39, 0x51, 0xEC, 0x7E, 0x93, 0x7B, 0x16, 0x52, 0xC0, 0xBD, 0x3B, 0xB1, + 0xBF, 0x07, 0x35, 0x73, 0xDF, 0x88, 0x3D, 0x2C, 0x34, 0xF1, 0xEF, 0x45, 0x1F, 0xD4, 0x6B, 0x50, + 0x3F, 0x00 }; +static const uint8_t p521_Gx[] = { 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFA, 0x51, 0x86, 0x87, 0x83, 0xBF, 0x2F, 0x96, 0x6B, 0x7F, 0xCC, 0x01, 0x48, 0xF7, 0x09, + 0xA5, 0xD0, 0x3B, 0xB5, 0xC9, 0xB8, 0x89, 0x9C, 0x47, 0xAE, 0xBB, 0x6F, 0xB7, 0x1E, 0x91, 0x38, + 0x64, 0x09 }; +static const uint8_t p521_Gy[] = { 0x00, 0xC6, 0x85, 0x8E, 0x06, 0xB7, 0x04, 0x04, 0xE9, 0xCD, 0x9E, 0x3E, 0xCB, 0x66, 0x23, 0x95, + 0xB4, 0x42, 0x9C, 0x64, 0x81, 0x39, 0x05, 0x3F, 0xB5, 0x21, 0xF8, 0x28, 0xAF, 0x60, 0x6B, 0x4D, + 0x3D, 0xBA, 0xA1, 0x4B, 0x5E, 0x77, 0xEF, 0xE7, 0x59, 0x28, 0xFE, 0x1D, 0xC1, 0x27, 0xA2, 0xFF, + 0xA8, 0xDE, 0x33, 0x48, 0xB3, 0xC1, 0x85, 0x6A, 0x42, 0x9B, 0xF9, 0x7E, 0x7E, 0x31, 0xC2, 0xE5, + 0xBD, 0x66 }; +static const uint8_t p521_n[] = { 0x01, 0x18, 0x39, 0x29, 0x6A, 0x78, 0x9A, 0x3B, 0xC0, 0x04, 0x5C, 0x8A, 0x5F, 0xB4, 0x2C, 0x7D, + 0x1B, 0xD9, 0x98, 0xF5, 0x44, 0x49, 0x57, 0x9B, 0x44, 0x68, 0x17, 0xAF, 0xBD, 0x17, 0x27, 0x3E, + 0x66, 0x2C, 0x97, 0xEE, 0x72, 0x99, 0x5E, 0xF4, 0x26, 0x40, 0xC5, 0x50, 0xB9, 0x01, 0x3F, 0xAD, + 0x07, 0x61, 0x35, 0x3C, 0x70, 0x86, 0xA2, 0x72, 0xC2, 0x40, 0x88, 0xBE, 0x94, 0x76, 0x9F, 0xD1, + 0x66, 0x50 }; +static const uint8_t p521_oid[] = { 0x2b, 0x81, 0x04, 0x00, 0x23 }; @@ -39,23 +39,23 @@ * 3 bits segment selector | up to 8 segments * 5 bits core selector | up to 32 cores/segment (see note below) * 8 bits register selector | up to 256 registers/core (see modexp below) - * + * * i.e, the address is structured as: * sss ccccc rrrrrrrr - * + * * The I2C and UART communication channels use this 16-bit address format * directly in their read and write commands. - * + * * The EIM communications channel translates this 16-bit address into a * 32-bit memory-mapped address in the range 0x08000000..807FFFF: * 00001000000000 sss 0 ccccc rrrrrrrr 00 - * + * * EIM, as implemented on the Novena, uses a 19-bit address space: * Bits 18..16 are the semgent selector. * Bits 15..10 are the core selector. * Bits 9..2 are the register selector. * Bits 1..0 are zero, because reads and writes are always word aligned. - * + * * Note that EIM can support 64 cores per segment, but we sacrifice one bit * in order to map it into a 16-bit address space. */ @@ -440,6 +440,8 @@ DEFINE_HAL_ERROR(HAL_ERROR_ALLOCATION_FAILURE, "Memory allocation failed") \ DEFINE_HAL_ERROR(HAL_ERROR_RESULT_TOO_LONG, "Result too long for buffer") \ DEFINE_HAL_ERROR(HAL_ERROR_ASN1_PARSE_FAILED, "ASN.1 parse failed") \ + DEFINE_HAL_ERROR(HAL_ERROR_KEY_NOT_ON_CURVE, "EC key is not on its purported curve") \ + DEFINE_HAL_ERROR(HAL_ERROR_INVALID_SIGNATURE, "Invalid signature") \ END_OF_HAL_ERROR_LIST /* Marker to forestall silly line continuation errors */ @@ -495,6 +497,12 @@ extern hal_error_t hal_get_random(void *buffer, const size_t length); #define HAL_MAX_HASH_DIGEST_LENGTH SHA512_DIGEST_LEN /* + * Opaque driver structure for digest algorithms. + */ + +typedef struct hal_hash_driver hal_hash_driver_t; + +/* * Public information about a digest algorithm. * * The _state_length values in the descriptor and the typed opaque @@ -510,16 +518,16 @@ typedef struct { size_t hmac_state_length; const uint8_t * const digest_algorithm_id; size_t digest_algorithm_id_length; - const void *driver; + const hal_hash_driver_t *driver; unsigned can_restore_state : 1; } hal_hash_descriptor_t; /* - * Typed opaque pointers to internal state. + * Opaque pointers to internal state. */ -typedef struct { void *state; } hal_hash_state_t; -typedef struct { void *state; } hal_hmac_state_t; +typedef struct hal_hash_state hal_hash_state_t; +typedef struct hal_hmac_state hal_hmac_state_t; /* * Supported digest algorithms. These are one-element arrays so that @@ -542,28 +550,28 @@ extern void hal_hash_set_debug(int onoff); extern hal_error_t hal_hash_core_present(const hal_hash_descriptor_t * const descriptor); extern hal_error_t hal_hash_initialize(const hal_hash_descriptor_t * const descriptor, - hal_hash_state_t *state, + hal_hash_state_t **state, void *state_buffer, const size_t state_length); -extern hal_error_t hal_hash_update(const hal_hash_state_t state, +extern hal_error_t hal_hash_update(hal_hash_state_t *state, const uint8_t * data, const size_t length); -extern hal_error_t hal_hash_finalize(const hal_hash_state_t state, +extern hal_error_t hal_hash_finalize(hal_hash_state_t *state, uint8_t *digest, const size_t length); extern hal_error_t hal_hmac_initialize(const hal_hash_descriptor_t * const descriptor, - hal_hmac_state_t *state, + hal_hmac_state_t **state, void *state_buffer, const size_t state_length, const uint8_t * const key, const size_t key_length); -extern hal_error_t hal_hmac_update(const hal_hmac_state_t state, +extern hal_error_t hal_hmac_update(hal_hmac_state_t *state, const uint8_t * data, const size_t length); -extern hal_error_t hal_hmac_finalize(const hal_hmac_state_t state, +extern hal_error_t hal_hmac_finalize(hal_hmac_state_t *state, uint8_t *hmac, const size_t length); -extern void hal_hash_cleanup(hal_hash_state_t *state); +extern void hal_hash_cleanup(hal_hash_state_t **state); -extern void hal_hmac_cleanup(hal_hmac_state_t *state); +extern void hal_hmac_cleanup(hal_hmac_state_t **state); /* * AES key wrap functions. @@ -608,7 +616,7 @@ extern hal_error_t hal_modexp(const uint8_t * const msg, const size_t msg_len, / typedef enum { HAL_RSA_PRIVATE, HAL_RSA_PUBLIC } hal_rsa_key_type_t; -typedef struct { void *key; } hal_rsa_key_t; +typedef struct hal_rsa_key hal_rsa_key_t; extern const size_t hal_rsa_key_t_size; @@ -616,7 +624,7 @@ extern void hal_rsa_set_debug(const int onoff); extern void hal_rsa_set_blinding(const int onoff); -extern hal_error_t hal_rsa_key_load_private(hal_rsa_key_t *key, +extern hal_error_t hal_rsa_key_load_private(hal_rsa_key_t **key, void *keybuf, const size_t keybuf_len, const uint8_t * const n, const size_t n_len, const uint8_t * const e, const size_t e_len, @@ -627,48 +635,122 @@ extern hal_error_t hal_rsa_key_load_private(hal_rsa_key_t *key, const uint8_t * const dP, const size_t dP_len, const uint8_t * const dQ, const size_t dQ_len); -extern hal_error_t hal_rsa_key_load_public(hal_rsa_key_t *key, +extern hal_error_t hal_rsa_key_load_public(hal_rsa_key_t **key, void *keybuf, const size_t keybuf_len, const uint8_t * const n, const size_t n_len, const uint8_t * const e, const size_t e_len); -extern hal_error_t hal_rsa_key_get_type(hal_rsa_key_t key, +extern hal_error_t hal_rsa_key_get_type(const hal_rsa_key_t * const key, hal_rsa_key_type_t *key_type); -extern hal_error_t hal_rsa_key_get_modulus(hal_rsa_key_t key, +extern hal_error_t hal_rsa_key_get_modulus(const hal_rsa_key_t * const key, uint8_t *modulus, size_t *modulus_len, const size_t modulus_max); -extern hal_error_t hal_rsa_key_get_public_exponent(hal_rsa_key_t key, +extern hal_error_t hal_rsa_key_get_public_exponent(const hal_rsa_key_t * const key, uint8_t *public_exponent, size_t *public_exponent_len, const size_t public_exponent_max); -extern void hal_rsa_key_clear(hal_rsa_key_t key); +extern void hal_rsa_key_clear(hal_rsa_key_t *key); -extern hal_error_t hal_rsa_encrypt(hal_rsa_key_t key, +extern hal_error_t hal_rsa_encrypt(const hal_rsa_key_t * const key, const uint8_t * const input, const size_t input_len, uint8_t * output, const size_t output_len); -extern hal_error_t hal_rsa_decrypt(hal_rsa_key_t key, +extern hal_error_t hal_rsa_decrypt(const hal_rsa_key_t * const key, const uint8_t * const input, const size_t input_len, uint8_t * output, const size_t output_len); -extern hal_error_t hal_rsa_key_gen(hal_rsa_key_t *key, +extern hal_error_t hal_rsa_key_gen(hal_rsa_key_t **key, void *keybuf, const size_t keybuf_len, const unsigned key_length, const uint8_t * const public_exponent, const size_t public_exponent_len); -extern hal_error_t hal_rsa_key_to_der(hal_rsa_key_t key, +extern hal_error_t hal_rsa_key_to_der(const hal_rsa_key_t * const key, uint8_t *der, size_t *der_len, const size_t der_max); -extern size_t hal_rsa_key_to_der_len(hal_rsa_key_t key); +extern size_t hal_rsa_key_to_der_len(const hal_rsa_key_t * const key); -extern hal_error_t hal_rsa_key_from_der(hal_rsa_key_t *key, +extern hal_error_t hal_rsa_key_from_der(hal_rsa_key_t **key, void *keybuf, const size_t keybuf_len, const uint8_t * const der, const size_t der_len); +/* + * ECDSA. + */ + +typedef enum { HAL_ECDSA_PRIVATE, HAL_ECDSA_PUBLIC } hal_ecdsa_key_type_t; + +typedef enum { HAL_ECDSA_CURVE_P256, HAL_ECDSA_CURVE_P384, HAL_ECDSA_CURVE_P521 } hal_ecdsa_curve_t; + +typedef enum { HAL_ECDSA_SIGNATURE_FORMAT_ASN1, HAL_ECDSA_SIGNATURE_FORMAT_PKCS11 } hal_ecdsa_signature_format_t; + +typedef struct hal_ecdsa_key hal_ecdsa_key_t; + +extern const size_t hal_ecdsa_key_t_size; + +extern void hal_ecdsa_set_debug(const int onoff); + +extern hal_error_t hal_ecdsa_key_load_private(hal_ecdsa_key_t **key, + void *keybuf, const size_t keybuf_len, + const hal_ecdsa_curve_t curve, + const uint8_t * const x, const size_t x_len, + const uint8_t * const y, const size_t y_len, + const uint8_t * const d, const size_t d_len); + +extern hal_error_t hal_ecdsa_key_load_public(hal_ecdsa_key_t **key, + void *keybuf, const size_t keybuf_len, + const hal_ecdsa_curve_t curve, + const uint8_t * const x, const size_t x_len, + const uint8_t * const y, const size_t y_len); + +extern hal_error_t hal_ecdsa_key_get_type(const hal_ecdsa_key_t * const key, + hal_ecdsa_key_type_t *key_type); + +extern hal_error_t hal_ecdsa_key_get_curve(const hal_ecdsa_key_t * const key, + hal_ecdsa_curve_t *curve); + +extern hal_error_t hal_ecdsa_key_get_public(const hal_ecdsa_key_t * const key, + uint8_t *x, size_t *x_len, const size_t x_max, + uint8_t *y, size_t *y_len, const size_t y_max); + +extern void hal_ecdsa_key_clear(hal_ecdsa_key_t *key); + +extern hal_error_t hal_ecdsa_key_gen(hal_ecdsa_key_t **key, + void *keybuf, const size_t keybuf_len, + const hal_ecdsa_curve_t curve); + +extern hal_error_t hal_ecdsa_key_to_der(const hal_ecdsa_key_t * const key, + uint8_t *der, size_t *der_len, const size_t der_max); + +extern size_t hal_ecdsa_key_to_der_len(const hal_ecdsa_key_t * const key); + +extern hal_error_t hal_ecdsa_key_from_der(hal_ecdsa_key_t **key, + void *keybuf, const size_t keybuf_len, + const uint8_t * const der, const size_t der_len); + +extern hal_error_t hal_ecdsa_key_to_ecpoint(const hal_ecdsa_key_t * const key, + uint8_t *der, size_t *der_len, const size_t der_max); + +extern size_t hal_ecdsa_key_to_ecpoint_len(const hal_ecdsa_key_t * const key); + +extern hal_error_t hal_ecdsa_key_from_ecpoint(hal_ecdsa_key_t **key, + void *keybuf, const size_t keybuf_len, + const uint8_t * const der, const size_t der_len, + const hal_ecdsa_curve_t curve); + +extern hal_error_t hal_ecdsa_sign(const hal_ecdsa_key_t * const key, + const uint8_t * const hash, const size_t hash_len, + uint8_t *signature, size_t *signature_len, const size_t signature_max, + const hal_ecdsa_signature_format_t signature_format); + +extern hal_error_t hal_ecdsa_verify(const hal_ecdsa_key_t * const key, + const uint8_t * const hash, const size_t hash_len, + const uint8_t * const signature, const size_t signature_len, + const hal_ecdsa_signature_format_t signature_format); + #endif /* _HAL_H_ */ /* diff --git a/hal_io_eim.c b/hal_io_eim.c index bdc3171..3687b95 100644 --- a/hal_io_eim.c +++ b/hal_io_eim.c @@ -1,11 +1,11 @@ -/* +/* * hal_io_eim.c * ------------ * This module contains common code to talk to the FPGA over the EIM bus. - * + * * Author: Paul Selkirk * Copyright (c) 2014-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: diff --git a/hal_io_i2c.c b/hal_io_i2c.c index c98ea7d..9788232 100644 --- a/hal_io_i2c.c +++ b/hal_io_i2c.c @@ -1,11 +1,11 @@ -/* +/* * hal_io_i2c.c * ------------ * This module contains common code to talk to the FPGA over the I2C bus. - * + * * Author: Paul Selkirk * Copyright (c) 2014-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: @@ -1,34 +1,34 @@ -/* +/* * hashes.c * -------- * HAL interface to Cryptech hash cores. - * + * * Authors: Joachim Strömbergson, Paul Selkirk, Rob Austein * Copyright (c) 2014-2015, SUNET - * - * 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 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 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * + * 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 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 OWNER 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 + * 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. */ @@ -64,7 +64,7 @@ * memory map, so it's not really worth overthinking at the moment. */ -typedef struct { +struct hal_hash_driver { size_t length_length; /* Length of the length field */ off_t block_addr; /* Where to write hash blocks */ off_t ctrl_addr; /* Control register */ @@ -73,16 +73,16 @@ typedef struct { off_t name_addr; /* Where to read core name */ char core_name[8]; /* Expected name of core */ uint8_t ctrl_mode; /* Digest mode, for cores that have modes */ -} driver_t; +}; /* * Hash state. For now we assume that the only core state we need to * save and restore is the current digest value. */ -typedef struct { +struct hal_hash_state { const hal_hash_descriptor_t *descriptor; - const driver_t *driver; + const hal_hash_driver_t *driver; uint64_t msg_length_high; /* Total data hashed in this message */ uint64_t msg_length_low; /* (128 bits in SHA-512 cases) */ uint8_t block[HAL_MAX_HASH_BLOCK_LENGTH], /* Block we're accumulating */ @@ -90,7 +90,7 @@ typedef struct { size_t block_used; /* How much of the block we've used */ unsigned block_count; /* Blocks sent */ unsigned flags; -} internal_hash_state_t; +}; #define STATE_FLAG_STATE_ALLOCATED 0x1 /* State buffer dynamically allocated */ @@ -102,10 +102,10 @@ typedef struct { * performance boost for things like PBKDF2. */ -typedef struct { - internal_hash_state_t hash_state; /* Hash state */ +struct hal_hmac_state { + hal_hash_state_t hash_state; /* Hash state */ uint8_t keybuf[HAL_MAX_HASH_BLOCK_LENGTH]; /* HMAC key */ -} internal_hmac_state_t; +}; /* * Drivers for known digest algorithms. @@ -115,42 +115,42 @@ typedef struct { * whine if the resulting string doesn't fit into the field. */ -static const driver_t sha1_driver = { +static const hal_hash_driver_t sha1_driver = { SHA1_LENGTH_LEN, SHA1_ADDR_BLOCK, SHA1_ADDR_CTRL, SHA1_ADDR_STATUS, SHA1_ADDR_DIGEST, SHA1_ADDR_NAME0, (SHA1_NAME0 SHA1_NAME1), 0 }; -static const driver_t sha256_driver = { +static const hal_hash_driver_t sha256_driver = { SHA256_LENGTH_LEN, SHA256_ADDR_BLOCK, SHA256_ADDR_CTRL, SHA256_ADDR_STATUS, SHA256_ADDR_DIGEST, SHA256_ADDR_NAME0, (SHA256_NAME0 SHA256_NAME1), 0 }; -static const driver_t sha512_224_driver = { +static const hal_hash_driver_t sha512_224_driver = { SHA512_LENGTH_LEN, SHA512_ADDR_BLOCK, SHA512_ADDR_CTRL, SHA512_ADDR_STATUS, SHA512_ADDR_DIGEST, SHA512_ADDR_NAME0, (SHA512_NAME0 SHA512_NAME1), MODE_SHA_512_224 }; -static const driver_t sha512_256_driver = { +static const hal_hash_driver_t sha512_256_driver = { SHA512_LENGTH_LEN, SHA512_ADDR_BLOCK, SHA512_ADDR_CTRL, SHA512_ADDR_STATUS, SHA512_ADDR_DIGEST, SHA512_ADDR_NAME0, (SHA512_NAME0 SHA512_NAME1), MODE_SHA_512_256 }; -static const driver_t sha384_driver = { +static const hal_hash_driver_t sha384_driver = { SHA512_LENGTH_LEN, SHA512_ADDR_BLOCK, SHA512_ADDR_CTRL, SHA512_ADDR_STATUS, SHA512_ADDR_DIGEST, SHA512_ADDR_NAME0, (SHA512_NAME0 SHA512_NAME1), MODE_SHA_384 }; -static const driver_t sha512_driver = { +static const hal_hash_driver_t sha512_driver = { SHA512_LENGTH_LEN, SHA512_ADDR_BLOCK, SHA512_ADDR_CTRL, SHA512_ADDR_STATUS, SHA512_ADDR_DIGEST, SHA512_ADDR_NAME0, (SHA512_NAME0 SHA512_NAME1), @@ -188,42 +188,42 @@ static const uint8_t const hal_hash_descriptor_t hal_hash_sha1[1] = {{ SHA1_BLOCK_LEN, SHA1_DIGEST_LEN, - sizeof(internal_hash_state_t), sizeof(internal_hmac_state_t), + sizeof(hal_hash_state_t), sizeof(hal_hmac_state_t), dalgid_sha1, sizeof(dalgid_sha1), &sha1_driver, 0 }}; const hal_hash_descriptor_t hal_hash_sha256[1] = {{ SHA256_BLOCK_LEN, SHA256_DIGEST_LEN, - sizeof(internal_hash_state_t), sizeof(internal_hmac_state_t), + sizeof(hal_hash_state_t), sizeof(hal_hmac_state_t), dalgid_sha256, sizeof(dalgid_sha256), &sha256_driver, 1 }}; const hal_hash_descriptor_t hal_hash_sha512_224[1] = {{ SHA512_BLOCK_LEN, SHA512_224_DIGEST_LEN, - sizeof(internal_hash_state_t), sizeof(internal_hmac_state_t), + sizeof(hal_hash_state_t), sizeof(hal_hmac_state_t), dalgid_sha512_224, sizeof(dalgid_sha512_224), &sha512_224_driver, 0 }}; const hal_hash_descriptor_t hal_hash_sha512_256[1] = {{ SHA512_BLOCK_LEN, SHA512_256_DIGEST_LEN, - sizeof(internal_hash_state_t), sizeof(internal_hmac_state_t), + sizeof(hal_hash_state_t), sizeof(hal_hmac_state_t), dalgid_sha512_256, sizeof(dalgid_sha512_256), &sha512_256_driver, 0 }}; const hal_hash_descriptor_t hal_hash_sha384[1] = {{ SHA512_BLOCK_LEN, SHA384_DIGEST_LEN, - sizeof(internal_hash_state_t), sizeof(internal_hmac_state_t), + sizeof(hal_hash_state_t), sizeof(hal_hmac_state_t), dalgid_sha384, sizeof(dalgid_sha384), &sha384_driver, 0 }}; const hal_hash_descriptor_t hal_hash_sha512[1] = {{ SHA512_BLOCK_LEN, SHA512_DIGEST_LEN, - sizeof(internal_hash_state_t), sizeof(internal_hmac_state_t), + sizeof(hal_hash_state_t), sizeof(hal_hmac_state_t), dalgid_sha512, sizeof(dalgid_sha512), &sha512_driver, 0 }}; @@ -247,7 +247,7 @@ void hal_hash_set_debug(int onoff) * Returns the driver pointer on success, NULL on failure. */ -static const driver_t *check_driver(const hal_hash_descriptor_t * const descriptor) +static const hal_hash_driver_t *check_driver(const hal_hash_descriptor_t * const descriptor) { return descriptor == NULL ? NULL : descriptor->driver; } @@ -258,7 +258,7 @@ static const driver_t *check_driver(const hal_hash_descriptor_t * const descript hal_error_t hal_hash_core_present(const hal_hash_descriptor_t * const descriptor) { - const driver_t * const driver = check_driver(descriptor); + const hal_hash_driver_t * const driver = check_driver(descriptor); if (driver == NULL) return HAL_ERROR_BAD_ARGUMENTS; @@ -273,13 +273,13 @@ hal_error_t hal_hash_core_present(const hal_hash_descriptor_t * const descriptor */ hal_error_t hal_hash_initialize(const hal_hash_descriptor_t * const descriptor, - hal_hash_state_t *opaque_state, + hal_hash_state_t **state_, void *state_buffer, const size_t state_length) { - const driver_t * const driver = check_driver(descriptor); - internal_hash_state_t *state = state_buffer; + const hal_hash_driver_t * const driver = check_driver(descriptor); + hal_hash_state_t *state = state_buffer; - if (driver == NULL || opaque_state == NULL) + if (driver == NULL || state_ == NULL) return HAL_ERROR_BAD_ARGUMENTS; if (state_buffer != NULL && state_length < descriptor->hash_state_length) @@ -295,7 +295,7 @@ hal_error_t hal_hash_initialize(const hal_hash_descriptor_t * const descriptor, if (state_buffer == NULL) state->flags |= STATE_FLAG_STATE_ALLOCATED; - opaque_state->state = state; + *state_ = state; return HAL_OK; } @@ -304,19 +304,19 @@ hal_error_t hal_hash_initialize(const hal_hash_descriptor_t * const descriptor, * Clean up hash state. No-op unless memory was dynamically allocated. */ -void hal_hash_cleanup(hal_hash_state_t *opaque_state) +void hal_hash_cleanup(hal_hash_state_t **state_) { - if (opaque_state == NULL) + if (state_ == NULL) return; - internal_hash_state_t *state = opaque_state->state; + hal_hash_state_t *state = *state_; if (state == NULL || (state->flags & STATE_FLAG_STATE_ALLOCATED) == 0) return; memset(state, 0, state->descriptor->hash_state_length); free(state); - opaque_state->state = NULL; + *state_ = NULL; } /* @@ -324,7 +324,7 @@ void hal_hash_cleanup(hal_hash_state_t *opaque_state) * read current hash state from core. */ -static hal_error_t hash_read_digest(const driver_t * const driver, +static hal_error_t hash_read_digest(const hal_hash_driver_t * const driver, uint8_t *digest, const size_t digest_length) { @@ -342,7 +342,7 @@ static hal_error_t hash_read_digest(const driver_t * const driver, * Write hash state back to core. */ -static hal_error_t hash_write_digest(const driver_t * const driver, +static hal_error_t hash_write_digest(const hal_hash_driver_t * const driver, const uint8_t * const digest, const size_t digest_length) { @@ -360,7 +360,7 @@ static hal_error_t hash_write_digest(const driver_t * const driver, * Send one block to a core. */ -static hal_error_t hash_write_block(internal_hash_state_t *state) +static hal_error_t hash_write_block(hal_hash_state_t * const state) { uint8_t ctrl_cmd[4]; hal_error_t err; @@ -388,7 +388,7 @@ static hal_error_t hash_write_block(internal_hash_state_t *state) return err; ctrl_cmd[0] = ctrl_cmd[1] = ctrl_cmd[2] = 0; - ctrl_cmd[3] = state->block_count == 0 ? CTRL_INIT : CTRL_NEXT; + ctrl_cmd[3] = state->block_count == 0 ? CTRL_INIT : CTRL_NEXT; ctrl_cmd[3] |= state->driver->ctrl_mode; if ((err = hal_io_write(state->driver->ctrl_addr, ctrl_cmd, sizeof(ctrl_cmd))) != HAL_OK) @@ -406,11 +406,10 @@ static hal_error_t hash_write_block(internal_hash_state_t *state) * Add data to hash. */ -hal_error_t hal_hash_update(hal_hash_state_t opaque_state, /* Opaque state block */ +hal_error_t hal_hash_update(hal_hash_state_t *state, /* Opaque state block */ const uint8_t * const data_buffer, /* Data to be hashed */ size_t data_buffer_length) /* Length of data_buffer */ { - internal_hash_state_t *state = opaque_state.state; const uint8_t *p = data_buffer; hal_error_t err; size_t n; @@ -463,11 +462,10 @@ hal_error_t hal_hash_update(hal_hash_state_t opaque_state, /* Opaque state * Finish hash and return digest. */ -hal_error_t hal_hash_finalize(hal_hash_state_t opaque_state, /* Opaque state block */ +hal_error_t hal_hash_finalize(hal_hash_state_t *state, /* Opaque state block */ uint8_t *digest_buffer, /* Returned digest */ const size_t digest_buffer_length) /* Length of digest_buffer */ { - internal_hash_state_t *state = opaque_state.state; uint64_t bit_length_high, bit_length_low; hal_error_t err; uint8_t *p; @@ -543,17 +541,16 @@ hal_error_t hal_hash_finalize(hal_hash_state_t opaque_state, /* Opaqu */ hal_error_t hal_hmac_initialize(const hal_hash_descriptor_t * const descriptor, - hal_hmac_state_t *opaque_state, + hal_hmac_state_t **state_, void *state_buffer, const size_t state_length, const uint8_t * const key, const size_t key_length) { - const driver_t * const driver = check_driver(descriptor); - internal_hmac_state_t *state = state_buffer; - hal_hash_state_t oh; + const hal_hash_driver_t * const driver = check_driver(descriptor); + hal_hmac_state_t *state = state_buffer; hal_error_t err; int i; - if (driver == NULL || opaque_state == NULL) + if (descriptor == NULL || driver == NULL || state_ == NULL) return HAL_ERROR_BAD_ARGUMENTS; if (state_buffer != NULL && state_length < descriptor->hmac_state_length) @@ -562,7 +559,7 @@ hal_error_t hal_hmac_initialize(const hal_hash_descriptor_t * const descriptor, if (state_buffer == NULL && (state = malloc(descriptor->hmac_state_length)) == NULL) return HAL_ERROR_ALLOCATION_FAILURE; - internal_hash_state_t *h = &state->hash_state; + hal_hash_state_t *h = &state->hash_state; assert(descriptor->block_length <= sizeof(state->keybuf)); @@ -576,7 +573,8 @@ hal_error_t hal_hmac_initialize(const hal_hash_descriptor_t * const descriptor, return HAL_ERROR_UNSUPPORTED_KEY; #endif - if ((err = hal_hash_initialize(descriptor, &oh, h, sizeof(*h))) != HAL_OK) + if ((err = hal_hash_initialize(descriptor, &h, &state->hash_state, + sizeof(state->hash_state))) != HAL_OK) goto fail; if (state_buffer == NULL) @@ -593,9 +591,10 @@ hal_error_t hal_hmac_initialize(const hal_hash_descriptor_t * const descriptor, if (key_length <= descriptor->block_length) memcpy(state->keybuf, key, key_length); - else if ((err = hal_hash_update(oh, key, key_length)) != HAL_OK || - (err = hal_hash_finalize(oh, state->keybuf, sizeof(state->keybuf))) != HAL_OK || - (err = hal_hash_initialize(descriptor, &oh, h, sizeof(*h))) != HAL_OK) + else if ((err = hal_hash_update(h, key, key_length)) != HAL_OK || + (err = hal_hash_finalize(h, state->keybuf, sizeof(state->keybuf))) != HAL_OK || + (err = hal_hash_initialize(descriptor, &h, &state->hash_state, + sizeof(state->hash_state))) != HAL_OK) goto fail; /* @@ -605,7 +604,7 @@ hal_error_t hal_hmac_initialize(const hal_hash_descriptor_t * const descriptor, for (i = 0; i < descriptor->block_length; i++) state->keybuf[i] ^= HMAC_IPAD; - if ((err = hal_hash_update(oh, state->keybuf, descriptor->block_length)) != HAL_OK) + if ((err = hal_hash_update(h, state->keybuf, descriptor->block_length)) != HAL_OK) goto fail; /* @@ -624,7 +623,7 @@ hal_error_t hal_hmac_initialize(const hal_hash_descriptor_t * const descriptor, * when the hash cores support such a thing. */ - opaque_state->state = state; + *state_ = state; return HAL_OK; @@ -638,61 +637,54 @@ hal_error_t hal_hmac_initialize(const hal_hash_descriptor_t * const descriptor, * Clean up HMAC state. No-op unless memory was dynamically allocated. */ -void hal_hmac_cleanup(hal_hmac_state_t *opaque_state) +void hal_hmac_cleanup(hal_hmac_state_t **state_) { - if (opaque_state == NULL) + if (state_ == NULL) return; - internal_hmac_state_t *state = opaque_state->state; + hal_hmac_state_t *state = *state_; if (state == NULL) return; - internal_hash_state_t *h = &state->hash_state; + hal_hash_state_t *h = &state->hash_state; if ((h->flags & STATE_FLAG_STATE_ALLOCATED) == 0) return; memset(state, 0, h->descriptor->hmac_state_length); free(state); - opaque_state->state = NULL; + *state_ = NULL; } /* * Add data to HMAC. */ -hal_error_t hal_hmac_update(const hal_hmac_state_t opaque_state, +hal_error_t hal_hmac_update(hal_hmac_state_t *state, const uint8_t * data, const size_t length) { - internal_hmac_state_t *state = opaque_state.state; - internal_hash_state_t *h = &state->hash_state; - hal_hash_state_t oh = { h }; - if (state == NULL || data == NULL) return HAL_ERROR_BAD_ARGUMENTS; - return hal_hash_update(oh, data, length); + return hal_hash_update(&state->hash_state, data, length); } /* * Finish and return HMAC. */ -hal_error_t hal_hmac_finalize(const hal_hmac_state_t opaque_state, +hal_error_t hal_hmac_finalize(hal_hmac_state_t *state, uint8_t *hmac, const size_t length) { - internal_hmac_state_t *state = opaque_state.state; - internal_hash_state_t *h = &state->hash_state; - const hal_hash_descriptor_t *descriptor; - hal_hash_state_t oh = { h }; - uint8_t d[HAL_MAX_HASH_DIGEST_LENGTH]; - hal_error_t err; - if (state == NULL || hmac == NULL) return HAL_ERROR_BAD_ARGUMENTS; - descriptor = h->descriptor; + hal_hash_state_t *h = &state->hash_state; + const hal_hash_descriptor_t *descriptor = h->descriptor; + uint8_t d[HAL_MAX_HASH_DIGEST_LENGTH]; + hal_error_t err; + assert(descriptor != NULL && descriptor->digest_length <= sizeof(d)); /* @@ -700,11 +692,12 @@ hal_error_t hal_hmac_finalize(const hal_hmac_state_t opaque_state, * to get HMAC. Key was prepared for this in hal_hmac_initialize(). */ - if ((err = hal_hash_finalize(oh, d, sizeof(d))) != HAL_OK || - (err = hal_hash_initialize(descriptor, &oh, h, sizeof(*h))) != HAL_OK || - (err = hal_hash_update(oh, state->keybuf, descriptor->block_length)) != HAL_OK || - (err = hal_hash_update(oh, d, descriptor->digest_length)) != HAL_OK || - (err = hal_hash_finalize(oh, hmac, length)) != HAL_OK) + if ((err = hal_hash_finalize(h, d, sizeof(d))) != HAL_OK || + (err = hal_hash_initialize(descriptor, &h, &state->hash_state, + sizeof(state->hash_state))) != HAL_OK || + (err = hal_hash_update(h, state->keybuf, descriptor->block_length)) != HAL_OK || + (err = hal_hash_update(h, d, descriptor->digest_length)) != HAL_OK || + (err = hal_hash_finalize(h, hmac, length)) != HAL_OK) return err; return HAL_OK; diff --git a/novena-eim.c b/novena-eim.c index c8c47ad..b55b01c 100644 --- a/novena-eim.c +++ b/novena-eim.c @@ -1,12 +1,12 @@ -/* +/* * novena-eim.c * ------------ * This module contains the userland magic to set up and use the EIM bus. * - * + * * Author: Pavel Shatov * Copyright (c) 2014-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: @@ -118,9 +118,9 @@ enum IMX6DQ_REGISTER_OFFSET IOMUXC_SW_PAD_CTL_PAD_EIM_AD15 = 0x020E0464, IOMUXC_SW_PAD_CTL_PAD_EIM_WAIT_B = 0x020E0468, IOMUXC_SW_PAD_CTL_PAD_EIM_BCLK = 0x020E046C, - + CCM_CCGR6 = 0x020C4080, - + EIM_CS0GCR1 = 0x021B8000, EIM_CS0GCR2 = 0x021B8004, EIM_CS0RCR1 = 0x021B8008, @@ -166,17 +166,17 @@ struct CCM_CCGR6 unsigned int cg1_usdhc1 : 2; unsigned int cg2_usdhc2 : 2; unsigned int cg3_usdhc3 : 2; - + unsigned int cg3_usdhc4 : 2; unsigned int cg5_eim_slow : 2; unsigned int cg6_vdoaxiclk : 2; unsigned int cg7_vpu : 2; - + unsigned int cg8_reserved : 2; unsigned int cg9_reserved : 2; unsigned int cg10_reserved : 2; unsigned int cg11_reserved : 2; - + unsigned int cg12_reserved : 2; unsigned int cg13_reserved : 2; unsigned int cg14_reserved : 2; @@ -58,7 +58,7 @@ static hal_error_t do_hmac(const hal_hash_descriptor_t * const d, assert(d != NULL && pw != NULL && data != NULL && mac != NULL); uint8_t sb[d->hmac_state_length]; - hal_hmac_state_t s; + hal_hmac_state_t *s; hal_error_t err; if ((err = hal_hmac_initialize(d, &s, sb, sizeof(sb), pw, pw_len)) != HAL_OK) @@ -9,7 +9,7 @@ * (but no simpler). * * Much of the code in this module is based, at least loosely, on Tom - * St Denis's libtomcrypt code. + * St Denis's libtomcrypt code. * * Authors: Rob Austein * Copyright (c) 2015, SUNET @@ -40,6 +40,25 @@ * 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. + * + * 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 <stdio.h> #include <stdint.h> #include <stdlib.h> @@ -48,6 +67,8 @@ #include <assert.h> #include "hal.h" +#include <tfm.h> +#include "asn1_internal.h" /* * Whether to use ModExp core. It works, but at the moment it's so @@ -59,22 +80,6 @@ #endif /* - * 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. - */ - -#include <tfm.h> - -/* * Whether we want debug output. */ @@ -103,7 +108,7 @@ void hal_rsa_set_blinding(const int onoff) * can make memory allocation the caller's problem. */ -struct rsa_key { +struct hal_rsa_key { hal_rsa_key_type_t type; /* What kind of key this is */ fp_int n; /* The modulus */ fp_int e; /* Public exponent */ @@ -115,7 +120,7 @@ struct rsa_key { fp_int dQ; /* d mod (q - 1) */ }; -const size_t hal_rsa_key_t_size = sizeof(struct rsa_key); +const size_t hal_rsa_key_t_size = sizeof(hal_rsa_key_t); /* * Error handling. @@ -139,19 +144,19 @@ const size_t hal_rsa_key_t_size = sizeof(struct rsa_key); * Unpack a bignum into a byte array, with length check. */ -static hal_error_t unpack_fp(fp_int *bn, uint8_t *buffer, const size_t length) +static hal_error_t unpack_fp(const fp_int * const bn, uint8_t *buffer, const size_t length) { hal_error_t err = HAL_OK; assert(bn != NULL && buffer != NULL); - const size_t bytes = fp_unsigned_bin_size(bn); + const size_t bytes = fp_unsigned_bin_size(unconst_fp_int(bn)); if (bytes > length) lose(HAL_ERROR_RESULT_TOO_LONG); memset(buffer, 0, length); - fp_to_unsigned_bin(bn, buffer + length - bytes); + fp_to_unsigned_bin(unconst_fp_int(bn), buffer + length - bytes); fail: return err; @@ -164,7 +169,10 @@ static hal_error_t unpack_fp(fp_int *bn, uint8_t *buffer, const size_t length) * wrap result back up as a bignum. */ -static hal_error_t modexp(fp_int *msg, fp_int *exp, fp_int *mod, fp_int *res) +static hal_error_t modexp(const fp_int * msg, + const fp_int * const exp, + const fp_int * const mod, + fp_int *res) { hal_error_t err = HAL_OK; @@ -172,14 +180,14 @@ static hal_error_t modexp(fp_int *msg, fp_int *exp, fp_int *mod, fp_int *res) fp_int reduced_msg; - if (fp_cmp_mag(msg, mod) != FP_LT) { + if (fp_cmp_mag(unconst_fp_int(msg), unconst_fp_int(mod)) != FP_LT) { fp_init(&reduced_msg); - fp_mod(msg, mod, &reduced_msg); + fp_mod(unconst_fp_int(msg), unconst_fp_int(mod), &reduced_msg); msg = &reduced_msg; } - const size_t exp_len = (fp_unsigned_bin_size(exp) + 3) & ~3; - const size_t mod_len = (fp_unsigned_bin_size(mod) + 3) & ~3; + const size_t exp_len = (fp_unsigned_bin_size(unconst_fp_int(exp)) + 3) & ~3; + const size_t mod_len = (fp_unsigned_bin_size(unconst_fp_int(mod)) + 3) & ~3; uint8_t msgbuf[mod_len]; uint8_t expbuf[exp_len]; @@ -227,10 +235,13 @@ int fp_exptmod(fp_int *a, fp_int *b, fp_int *c, fp_int *d) * wait for the slow FPGA implementation. */ -static hal_error_t modexp(fp_int *msg, fp_int *exp, fp_int *mod, fp_int *res) +static hal_error_t modexp(const fp_int * const msg, + const fp_int * const exp, + const fp_int * const mod, + fp_int *res) { hal_error_t err = HAL_OK; - FP_CHECK(fp_exptmod(msg, exp, mod, res)); + FP_CHECK(fp_exptmod(unconst_fp_int(msg), unconst_fp_int(exp), unconst_fp_int(mod), res)); fail: return err; } @@ -243,11 +254,11 @@ static hal_error_t modexp(fp_int *msg, fp_int *exp, fp_int *mod, fp_int *res) * try. Come back to this if it looks like a bottleneck. */ -static hal_error_t create_blinding_factors(struct rsa_key *key, fp_int *bf, fp_int *ubf) +static hal_error_t create_blinding_factors(const hal_rsa_key_t * const key, fp_int *bf, fp_int *ubf) { assert(key != NULL && bf != NULL && ubf != NULL); - uint8_t rnd[fp_unsigned_bin_size(&key->n)]; + uint8_t rnd[fp_unsigned_bin_size(unconst_fp_int(&key->n))]; hal_error_t err = HAL_OK; if ((err = hal_get_random(rnd, sizeof(rnd))) != HAL_OK) @@ -260,7 +271,7 @@ static hal_error_t create_blinding_factors(struct rsa_key *key, fp_int *bf, fp_i if ((err = modexp(bf, &key->e, &key->n, bf)) != HAL_OK) goto fail; - FP_CHECK(fp_invmod(ubf, &key->n, ubf)); + FP_CHECK(fp_invmod(ubf, unconst_fp_int(&key->n), ubf)); fail: memset(rnd, 0, sizeof(rnd)); @@ -271,7 +282,7 @@ static hal_error_t create_blinding_factors(struct rsa_key *key, fp_int *bf, fp_i * RSA decryption via Chinese Remainder Theorem (Garner's formula). */ -static hal_error_t rsa_crt(struct rsa_key *key, fp_int *msg, fp_int *sig) +static hal_error_t rsa_crt(const hal_rsa_key_t * const key, fp_int *msg, fp_int *sig) { assert(key != NULL && msg != NULL && sig != NULL); @@ -288,7 +299,7 @@ static hal_error_t rsa_crt(struct rsa_key *key, fp_int *msg, fp_int *sig) if (blinding) { if ((err = create_blinding_factors(key, &bf, &ubf)) != HAL_OK) goto fail; - FP_CHECK(fp_mulmod(msg, &bf, &key->n, msg)); + FP_CHECK(fp_mulmod(msg, &bf, unconst_fp_int(&key->n), msg)); } /* @@ -309,24 +320,24 @@ static hal_error_t rsa_crt(struct rsa_key *key, fp_int *msg, fp_int *sig) * once or twice doesn't help, something is very wrong. */ if (fp_cmp_d(&t, 0) == FP_LT) - fp_add(&t, &key->p, &t); + fp_add(&t, unconst_fp_int(&key->p), &t); if (fp_cmp_d(&t, 0) == FP_LT) - fp_add(&t, &key->p, &t); + fp_add(&t, unconst_fp_int(&key->p), &t); if (fp_cmp_d(&t, 0) == FP_LT) lose(HAL_ERROR_IMPOSSIBLE); /* * sig = (t * u mod p) * q + m2 */ - FP_CHECK(fp_mulmod(&t, &key->u, &key->p, &t)); - fp_mul(&t, &key->q, &t); + FP_CHECK(fp_mulmod(&t, unconst_fp_int(&key->u), unconst_fp_int(&key->p), &t)); + fp_mul(&t, unconst_fp_int(&key->q), &t); fp_add(&t, &m2, sig); /* * Unblind if necessary. */ if (blinding) - FP_CHECK(fp_mulmod(sig, &ubf, &key->n, sig)); + FP_CHECK(fp_mulmod(sig, &ubf, unconst_fp_int(&key->n), sig)); fail: fp_zero(&t); @@ -342,11 +353,10 @@ static hal_error_t rsa_crt(struct rsa_key *key, fp_int *msg, fp_int *sig) * to the caller. */ -hal_error_t hal_rsa_encrypt(hal_rsa_key_t key_, +hal_error_t hal_rsa_encrypt(const hal_rsa_key_t * const key, const uint8_t * const input, const size_t input_len, uint8_t * output, const size_t output_len) { - struct rsa_key *key = key_.key; hal_error_t err = HAL_OK; if (key == NULL || input == NULL || output == NULL || input_len > output_len) @@ -356,7 +366,7 @@ hal_error_t hal_rsa_encrypt(hal_rsa_key_t key_, fp_init(&i); fp_init(&o); - fp_read_unsigned_bin(&i, (uint8_t *) input, input_len); + fp_read_unsigned_bin(&i, unconst_uint8_t(input), input_len); if ((err = modexp(&i, &key->e, &key->n, &o)) != HAL_OK || (err = unpack_fp(&o, output, output_len)) != HAL_OK) @@ -368,11 +378,10 @@ hal_error_t hal_rsa_encrypt(hal_rsa_key_t key_, return err; } -hal_error_t hal_rsa_decrypt(hal_rsa_key_t key_, +hal_error_t hal_rsa_decrypt(const hal_rsa_key_t * const key, const uint8_t * const input, const size_t input_len, uint8_t * output, const size_t output_len) { - struct rsa_key *key = key_.key; hal_error_t err = HAL_OK; if (key == NULL || input == NULL || output == NULL || input_len > output_len) @@ -382,7 +391,7 @@ hal_error_t hal_rsa_decrypt(hal_rsa_key_t key_, fp_init(&i); fp_init(&o); - fp_read_unsigned_bin(&i, (uint8_t *) input, input_len); + fp_read_unsigned_bin(&i, unconst_uint8_t(input), input_len); /* * Do CRT if we have all the necessary key components, otherwise @@ -393,7 +402,7 @@ hal_error_t hal_rsa_decrypt(hal_rsa_key_t key_, err = modexp(&i, &key->d, &key->n, &o); else err = rsa_crt(key, &i, &o); - + if (err != HAL_OK || (err = unpack_fp(&o, output, output_len)) != HAL_OK) goto fail; @@ -408,10 +417,10 @@ hal_error_t hal_rsa_decrypt(hal_rsa_key_t key_, * than plain old memset() eventually. */ -void hal_rsa_key_clear(hal_rsa_key_t key) +void hal_rsa_key_clear(hal_rsa_key_t *key) { - if (key.key != NULL) - memset(key.key, 0, sizeof(struct rsa_key)); + if (key != NULL) + memset(key, 0, sizeof(*key)); } /* @@ -425,7 +434,7 @@ void hal_rsa_key_clear(hal_rsa_key_t key) */ static hal_error_t load_key(const hal_rsa_key_type_t type, - hal_rsa_key_t *key_, + hal_rsa_key_t **key_, void *keybuf, const size_t keybuf_len, const uint8_t * const n, const size_t n_len, const uint8_t * const e, const size_t e_len, @@ -436,22 +445,22 @@ static hal_error_t load_key(const hal_rsa_key_type_t type, const uint8_t * const dP, const size_t dP_len, const uint8_t * const dQ, const size_t dQ_len) { - if (key_ == NULL || keybuf == NULL || keybuf_len < sizeof(struct rsa_key)) + if (key_ == NULL || keybuf == NULL || keybuf_len < sizeof(hal_rsa_key_t)) return HAL_ERROR_BAD_ARGUMENTS; memset(keybuf, 0, keybuf_len); - struct rsa_key *key = keybuf; + hal_rsa_key_t *key = keybuf; key->type = type; -#define _(x) do { fp_init(&key->x); if (x == NULL) goto fail; fp_read_unsigned_bin(&key->x, (uint8_t *) x, x##_len); } while (0) +#define _(x) do { fp_init(&key->x); if (x == NULL) goto fail; fp_read_unsigned_bin(&key->x, unconst_uint8_t(x), x##_len); } while (0) switch (type) { case HAL_RSA_PRIVATE: _(d); _(p); _(q); _(u); _(dP); _(dQ); case HAL_RSA_PUBLIC: _(n); _(e); - key_->key = key; + *key_ = key; return HAL_OK; } #undef _ @@ -465,7 +474,7 @@ static hal_error_t load_key(const hal_rsa_key_type_t type, * Public API to load_key(). */ -hal_error_t hal_rsa_key_load_private(hal_rsa_key_t *key_, +hal_error_t hal_rsa_key_load_private(hal_rsa_key_t **key_, void *keybuf, const size_t keybuf_len, const uint8_t * const n, const size_t n_len, const uint8_t * const e, const size_t e_len, @@ -481,7 +490,7 @@ hal_error_t hal_rsa_key_load_private(hal_rsa_key_t *key_, d, d_len, p, p_len, q, q_len, u, u_len, dP, dP_len, dQ, dQ_len); } -hal_error_t hal_rsa_key_load_public(hal_rsa_key_t *key_, +hal_error_t hal_rsa_key_load_public(hal_rsa_key_t **key_, void *keybuf, const size_t keybuf_len, const uint8_t * const n, const size_t n_len, const uint8_t * const e, const size_t e_len) @@ -495,11 +504,9 @@ hal_error_t hal_rsa_key_load_public(hal_rsa_key_t *key_, * Extract the key type. */ -hal_error_t hal_rsa_key_get_type(hal_rsa_key_t key_, +hal_error_t hal_rsa_key_get_type(const hal_rsa_key_t * const key, hal_rsa_key_type_t *key_type) { - struct rsa_key *key = key_.key; - if (key == NULL || key_type == NULL) return HAL_ERROR_BAD_ARGUMENTS; @@ -511,15 +518,16 @@ hal_error_t hal_rsa_key_get_type(hal_rsa_key_t key_, * Extract public key components. */ -static hal_error_t extract_component(hal_rsa_key_t key_, const size_t offset, +static hal_error_t extract_component(const hal_rsa_key_t * const key, + const size_t offset, uint8_t *res, size_t *res_len, const size_t res_max) { - if (key_.key == NULL) + if (key == NULL) return HAL_ERROR_BAD_ARGUMENTS; - fp_int *bn = (fp_int *) (((uint8_t *) key_.key) + offset); + const fp_int * const bn = (const fp_int *) (((const uint8_t *) key) + offset); - const size_t len = fp_unsigned_bin_size(bn); + const size_t len = fp_unsigned_bin_size(unconst_fp_int(bn)); if (res_len != NULL) *res_len = len; @@ -531,31 +539,33 @@ static hal_error_t extract_component(hal_rsa_key_t key_, const size_t offset, return HAL_ERROR_RESULT_TOO_LONG; memset(res, 0, res_max); - fp_to_unsigned_bin(bn, res); + fp_to_unsigned_bin(unconst_fp_int(bn), res); return HAL_OK; } -hal_error_t hal_rsa_key_get_modulus(hal_rsa_key_t key_, +hal_error_t hal_rsa_key_get_modulus(const hal_rsa_key_t * const key, uint8_t *res, size_t *res_len, const size_t res_max) { - return extract_component(key_, offsetof(struct rsa_key, n), res, res_len, res_max); + return extract_component(key, offsetof(hal_rsa_key_t, n), res, res_len, res_max); } -hal_error_t hal_rsa_key_get_public_exponent(hal_rsa_key_t key_, +hal_error_t hal_rsa_key_get_public_exponent(const hal_rsa_key_t * const key, uint8_t *res, size_t *res_len, const size_t res_max) { - return extract_component(key_, offsetof(struct rsa_key, e), res, res_len, res_max); + return extract_component(key, offsetof(hal_rsa_key_t, e), res, res_len, res_max); } /* * Generate a prime factor for an RSA keypair. - * + * * Get random bytes, munge a few bits, and stuff into a bignum. Keep * doing this until we find a result that's (probably) prime and for * which result - 1 is relatively prime with respect to e. */ -static hal_error_t find_prime(unsigned prime_length, fp_int *e, fp_int *result) +static hal_error_t find_prime(const unsigned prime_length, + const fp_int * const e, + fp_int *result) { uint8_t buffer[prime_length]; hal_error_t err; @@ -571,7 +581,7 @@ static hal_error_t find_prime(unsigned prime_length, fp_int *e, fp_int *result) fp_read_unsigned_bin(result, buffer, sizeof(buffer)); } while (!fp_isprime(result) || - (fp_sub_d(result, 1, &t), fp_gcd(&t, e, &t), fp_cmp_d(&t, 1) != FP_EQ)); + (fp_sub_d(result, 1, &t), fp_gcd(&t, unconst_fp_int(e), &t), fp_cmp_d(&t, 1) != FP_EQ)); fp_zero(&t); return HAL_OK; @@ -581,16 +591,16 @@ static hal_error_t find_prime(unsigned prime_length, fp_int *e, fp_int *result) * Generate a new RSA keypair. */ -hal_error_t hal_rsa_key_gen(hal_rsa_key_t *key_, +hal_error_t hal_rsa_key_gen(hal_rsa_key_t **key_, void *keybuf, const size_t keybuf_len, const unsigned key_length, const uint8_t * const public_exponent, const size_t public_exponent_len) { - struct rsa_key *key = keybuf; + hal_rsa_key_t *key = keybuf; hal_error_t err = HAL_OK; fp_int p_1, q_1; - if (key_ == NULL || keybuf == NULL || keybuf_len < sizeof(struct rsa_key)) + if (key_ == NULL || keybuf == NULL || keybuf_len < sizeof(hal_rsa_key_t)) return HAL_ERROR_BAD_ARGUMENTS; memset(keybuf, 0, keybuf_len); @@ -624,7 +634,7 @@ hal_error_t hal_rsa_key_gen(hal_rsa_key_t *key_, FP_CHECK(fp_mod(&key->d, &q_1, &key->dQ)); /* dQ = d % (q-1) */ FP_CHECK(fp_invmod(&key->q, &key->p, &key->u)); /* u = (1/q) % p */ - key_->key = key; + *key_ = key; /* Fall through to cleanup */ @@ -637,19 +647,9 @@ hal_error_t hal_rsa_key_gen(hal_rsa_key_t *key_, } /* - * Minimal ASN.1 encoding and decoding for private keys. This is NOT - * a general-purpose ASN.1 implementation, just enough to read and - * write PKCS #1.5 RSAPrivateKey syntax (RFC 2313 section 7.2). + * Just enough ASN.1 to read and write PKCS #1.5 RSAPrivateKey syntax + * (RFC 2313 section 7.2). * - * If at some later date we need a full ASN.1 implementation we'll add - * it as (a) separate library module(s), but for now the goal is just - * to let us serialize private keys for internal use and debugging. - */ - -#define ASN1_INTEGER 0x02 -#define ASN1_SEQUENCE 0x30 - -/* * RSAPrivateKey fields in the required order. */ @@ -664,139 +664,9 @@ hal_error_t hal_rsa_key_gen(hal_rsa_key_t *key_, _(&key->dQ); \ _(&key->u); -static size_t count_length(size_t length) -{ - size_t result = 1; - - if (length >= 128) - for (; length > 0; length >>= 8) - result++; - - return result; -} - -static void encode_length(size_t length, size_t length_len, uint8_t *der) -{ - assert(der != NULL && length_len > 0 && length_len < 128); - - if (length < 128) { - assert(length_len == 1); - *der = (uint8_t) length; - } - - else { - *der = 0x80 | (uint8_t) --length_len; - while (length > 0 && length_len > 0) { - der[length_len--] = (uint8_t) (length & 0xFF); - length >>= 8; - } - assert(length == 0 && length_len == 0); - } -} - -static hal_error_t decode_header(const uint8_t tag, - const uint8_t * const der, size_t der_max, - size_t *hlen, size_t *vlen) -{ - assert(der != NULL && hlen != NULL && vlen != NULL); - - if (der_max < 2 || der[0] != tag) - return HAL_ERROR_ASN1_PARSE_FAILED; - - if ((der[1] & 0x80) == 0) { - *hlen = 2; - *vlen = der[1]; - } - - else { - *hlen = 2 + (der[1] & 0x7F); - *vlen = 0; - - if (*hlen > der_max) - return HAL_ERROR_ASN1_PARSE_FAILED; - - for (size_t i = 2; i < *hlen; i++) - *vlen = (*vlen << 8) + der[i]; - } - - if (*hlen + *vlen > der_max) - return HAL_ERROR_ASN1_PARSE_FAILED; - - return HAL_OK; -} - -static hal_error_t encode_integer(fp_int *bn, - uint8_t *der, size_t *der_len, const size_t der_max) -{ - if (bn == NULL || der_len == NULL) - return HAL_ERROR_BAD_ARGUMENTS; - - /* - * Calculate length. Need to pad data with a leading zero if most - * significant bit is set, to avoid flipping ASN.1 sign bit. If - * caller didn't supply a buffer, just return the total length. - */ - - const int cmp = fp_cmp_d(bn, 0); - - if (cmp != FP_EQ && cmp != FP_GT) - return HAL_ERROR_BAD_ARGUMENTS; - - const int leading_zero = (cmp == FP_EQ || (fp_count_bits(bn) & 7) == 0); - const size_t data_len = fp_unsigned_bin_size(bn) + leading_zero; - const size_t tag_len = 1; - const size_t length_len = count_length(data_len); - const size_t total_len = tag_len + length_len + data_len; - - *der_len = total_len; - - if (der == NULL) - return HAL_OK; - - if (total_len > der_max) - return HAL_ERROR_RESULT_TOO_LONG; - - /* - * Now encode. - */ - - *der++ = ASN1_INTEGER; - encode_length(data_len, length_len, der); - der += length_len; - if (leading_zero) - *der++ = 0x00; - fp_to_unsigned_bin(bn, der); - - return HAL_OK; -} - -static hal_error_t decode_integer(fp_int *bn, - const uint8_t * const der, size_t *der_len, const size_t der_max) -{ - if (bn == NULL || der == NULL) - return HAL_ERROR_BAD_ARGUMENTS; - - hal_error_t err; - size_t hlen, vlen; - - if ((err = decode_header(ASN1_INTEGER, der, der_max, &hlen, &vlen)) != HAL_OK) - return err; - - if (der_len != NULL) - *der_len = hlen + vlen; - - if (vlen < 1 || (der[hlen] & 0x80) != 0x00) - return HAL_ERROR_ASN1_PARSE_FAILED; - - fp_init(bn); - fp_read_unsigned_bin(bn, (uint8_t *) der + hlen, vlen); - return HAL_OK; -} - -hal_error_t hal_rsa_key_to_der(hal_rsa_key_t key_, +hal_error_t hal_rsa_key_to_der(const hal_rsa_key_t * const key, uint8_t *der, size_t *der_len, const size_t der_max) { - struct rsa_key *key = key_.key; hal_error_t err = HAL_OK; if (key == NULL || der_len == NULL || key->type != HAL_RSA_PRIVATE) @@ -806,65 +676,64 @@ hal_error_t hal_rsa_key_to_der(hal_rsa_key_t key_, fp_zero(&version); /* - * Calculate length. + * Calculate data length. */ - size_t data_len = 0; + size_t vlen = 0; -#define _(x) { size_t i; if ((err = encode_integer(x, NULL, &i, der_max - data_len)) != HAL_OK) return err; data_len += i; } +#define _(x) { size_t i; if ((err = hal_asn1_encode_integer(x, NULL, &i, der_max - vlen)) != HAL_OK) return err; vlen += i; } RSAPrivateKey_fields; #undef _ - const size_t tag_len = 1; - const size_t length_len = count_length(data_len); - const size_t total_len = tag_len + length_len + data_len; + /* + * Encode header. + */ - *der_len = total_len; + if ((err = hal_asn1_encode_header(ASN1_SEQUENCE, vlen, der, der_len, der_max)) != HAL_OK) + return err; + + const size_t hlen = *der_len; + *der_len += vlen; if (der == NULL) return HAL_OK; - if (total_len > der_max) - return HAL_ERROR_RESULT_TOO_LONG; - /* - * Now encode. + * Encode data. */ - *der++ = ASN1_SEQUENCE; - encode_length(data_len, length_len, der); - der += length_len; - -#define _(x) { size_t i; if ((err = encode_integer(x, der, &i, data_len)) != HAL_OK) return err; der += i; data_len -= i; } + der += hlen; + +#define _(x) { size_t i; if ((err = hal_asn1_encode_integer(x, der, &i, vlen)) != HAL_OK) return err; der += i; vlen -= i; } RSAPrivateKey_fields; #undef _ return HAL_OK; } -size_t hal_rsa_key_to_der_len(hal_rsa_key_t key_) +size_t hal_rsa_key_to_der_len(const hal_rsa_key_t * const key) { size_t len = 0; - return hal_rsa_key_to_der(key_, NULL, &len, 0) == HAL_OK ? len : 0; + return hal_rsa_key_to_der(key, NULL, &len, 0) == HAL_OK ? len : 0; } -hal_error_t hal_rsa_key_from_der(hal_rsa_key_t *key_, +hal_error_t hal_rsa_key_from_der(hal_rsa_key_t **key_, void *keybuf, const size_t keybuf_len, const uint8_t *der, const size_t der_len) { - if (key_ == NULL || keybuf == NULL || keybuf_len < sizeof(struct rsa_key) || der == NULL) + if (key_ == NULL || keybuf == NULL || keybuf_len < sizeof(hal_rsa_key_t) || der == NULL) return HAL_ERROR_BAD_ARGUMENTS; memset(keybuf, 0, keybuf_len); - struct rsa_key *key = keybuf; + hal_rsa_key_t *key = keybuf; key->type = HAL_RSA_PRIVATE; hal_error_t err = HAL_OK; size_t hlen, vlen; - if ((err = decode_header(ASN1_SEQUENCE, der, der_len, &hlen, &vlen)) != HAL_OK) + if ((err = hal_asn1_decode_header(ASN1_SEQUENCE, der, der_len, &hlen, &vlen)) != HAL_OK) return err; der += hlen; @@ -872,14 +741,14 @@ hal_error_t hal_rsa_key_from_der(hal_rsa_key_t *key_, fp_int version; fp_init(&version); -#define _(x) { size_t i; if ((err = decode_integer(x, der, &i, vlen)) != HAL_OK) return err; der += i; vlen -= i; } +#define _(x) { size_t i; if ((err = hal_asn1_decode_integer(x, der, &i, vlen)) != HAL_OK) return err; der += i; vlen -= i; } RSAPrivateKey_fields; #undef _ if (fp_cmp_d(&version, 0) != FP_EQ) return HAL_ERROR_ASN1_PARSE_FAILED; - key_->key = key; + *key_ = key; return HAL_OK; } diff --git a/tests/GNUmakefile b/tests/GNUmakefile index 307f23e..a1cd4b4 100644 --- a/tests/GNUmakefile +++ b/tests/GNUmakefile @@ -27,7 +27,7 @@ INC = ../hal.h LIB = ../libhal.a -BIN = test-aes-key-wrap test-hash test-pbkdf2 test-rsa +BIN = test-aes-key-wrap test-hash test-pbkdf2 test-rsa test-ecdsa CFLAGS = -g3 -Wall -fPIC -std=c99 -I.. diff --git a/tests/test-ecdsa.c b/tests/test-ecdsa.c new file mode 100644 index 0000000..cb590e5 --- /dev/null +++ b/tests/test-ecdsa.c @@ -0,0 +1,326 @@ +/* + * test-ecdsa.c + * ------------ + * Test harness for Cryptech ECDSA code. + * + * At the moment, the ECDSA code is a pure software implementation, + * Verilog will be along eventually. + * + * Testing ECDSA is a bit tricky because ECDSA depends heavily on + * using a new random secret for each signature. So we can test some + * things against the normal ECDSA implemenation, but some tests + * require a side door replacement of the random number generator so + * that we can use a known values from our test vector in place of the + * random secret that would be used in real operation. Test code for + * the latter mode depends on the library having been compiled with + * the testing hook enable, which it should not be for production use. + * + * Authors: Rob Austein + * Copyright (c) 2015, SUNET + * + * 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 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 OWNER 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 <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> +#include <errno.h> + +#include <sys/time.h> + +#include <hal.h> + +#include "test-ecdsa.h" + +#if HAL_ECDSA_DEBUG_ONLY_STATIC_TEST_VECTOR_RANDOM + +/* + * Code to let us replace ECDSA's random numbers with test data, if + * the ECDSA library code has been compiled with support for this. + */ + +typedef hal_error_t (*rng_override_test_function_t)(void *, const size_t); + +extern rng_override_test_function_t hal_ecdsa_set_rng_override_test_function(rng_override_test_function_t new_func); + +static const uint8_t *next_random_value = NULL; +static size_t next_random_length = 0; + +static hal_error_t next_random_handler(void *data, const size_t length) +{ + if (data == NULL) + return HAL_ERROR_BAD_ARGUMENTS; + + if (next_random_value == NULL || length < next_random_length) + return HAL_ERROR_IMPOSSIBLE; + + memset(data, 0, length); + memcpy(data + length - next_random_length, next_random_value, next_random_length); + + next_random_value = NULL; + next_random_length = 0; + + (void) hal_ecdsa_set_rng_override_test_function(0); + + return HAL_OK; +} + +static void set_next_random(const uint8_t * const data, const size_t length) +{ + (void) hal_ecdsa_set_rng_override_test_function(next_random_handler); + next_random_value = data; + next_random_length = length; +} + +/* + * Run one keygen test from test vectors. + */ + +static int test_against_static_vectors(const ecdsa_tc_t * const tc) + +{ + hal_error_t err; + + printf("Starting static test vector tests for P-%lu\n", (unsigned long) (tc->d_len * 8)); + + set_next_random(tc->d, tc->d_len); + + uint8_t keybuf1[hal_ecdsa_key_t_size]; + hal_ecdsa_key_t *key1 = NULL; + + if ((err = hal_ecdsa_key_gen(&key1, keybuf1, sizeof(keybuf1), tc->curve)) != HAL_OK) + return printf("hal_ecdsa_key_gen() failed: %s\n", hal_error_string(err)), 0; + + uint8_t Qx[tc->Qx_len], Qy[tc->Qy_len]; + size_t Qx_len, Qy_len; + + if ((err = hal_ecdsa_key_get_public(key1, Qx, &Qx_len, sizeof(Qx), Qy, &Qy_len, sizeof(Qy))) != HAL_OK) + return printf("hal_ecdsa_key_get_public() failed: %s\n", hal_error_string(err)), 0; + + if (tc->Qx_len != Qx_len || memcmp(tc->Qx, Qx, Qx_len) != 0) + return printf("Qx mismatch\n"), 0; + + if (tc->Qy_len != Qy_len || memcmp(tc->Qy, Qy, Qy_len) != 0) + return printf("Qy mismatch\n"), 0; + + if (hal_ecdsa_key_to_der_len(key1) != tc->key_len) + return printf("DER Key length mismatch\n"), 0; + + uint8_t keyder[tc->key_len]; + size_t keyder_len; + + if ((err = hal_ecdsa_key_to_der(key1, keyder, &keyder_len, sizeof(keyder))) != HAL_OK) + return printf("hal_ecdsa_key_to_der() failed: %s\n", hal_error_string(err)), 0; + + uint8_t keybuf2[hal_ecdsa_key_t_size]; + hal_ecdsa_key_t *key2 = NULL; + + if ((err = hal_ecdsa_key_from_der(&key2, keybuf2, sizeof(keybuf2), keyder, keyder_len)) != HAL_OK) + return printf("hal_ecdsa_key_from_der() failed: %s\n", hal_error_string(err)), 0; + + if (memcmp(key1, key2, hal_ecdsa_key_t_size) != 0) + return printf("Key mismatch after read/write cycle\n"), 0; + + set_next_random(tc->k, tc->k_len); + + uint8_t sig[tc->sig_len + 4]; + size_t sig_len; + + if ((err = hal_ecdsa_sign(key1, tc->H, tc->H_len, sig, &sig_len, sizeof(sig), HAL_ECDSA_SIGNATURE_FORMAT_ASN1)) != HAL_OK) + return printf("hal_ecdsa_sign() failed: %s\n", hal_error_string(err)), 0; + + if (sig_len != tc->sig_len || memcmp(sig, tc->sig, tc->sig_len) != 0) + return printf("Signature mismatch\n"), 0; + + if ((err = hal_ecdsa_verify(key2, tc->H, tc->H_len, sig, sig_len, HAL_ECDSA_SIGNATURE_FORMAT_ASN1)) != HAL_OK) + return printf("hal_ecdsa_verify(private) failed: %s\n", hal_error_string(err)), 0; + + hal_ecdsa_key_clear(key2); + key2 = NULL; + + if ((err = hal_ecdsa_key_load_private(&key2, keybuf2, sizeof(keybuf2), tc->curve, + tc->Qx, tc->Qx_len, tc->Qy, tc->Qy_len, tc->d, tc->d_len)) != HAL_OK) + return printf("hal_ecdsa_load_private() failed: %s\n", hal_error_string(err)), 0; + + if (memcmp(key1, key2, hal_ecdsa_key_t_size) != 0) + return printf("Key mismatch after hal_ecdsa_load_private_key()\n"), 0; + + hal_ecdsa_key_clear(key2); + key2 = NULL; + + if ((err = hal_ecdsa_key_load_public(&key2, keybuf2, sizeof(keybuf2), tc->curve, + tc->Qx, tc->Qx_len, tc->Qy, tc->Qy_len)) != HAL_OK) + return printf("hal_ecdsa_load_public() failed: %s\n", hal_error_string(err)), 0; + + if ((err = hal_ecdsa_verify(key2, tc->H, tc->H_len, sig, sig_len, HAL_ECDSA_SIGNATURE_FORMAT_ASN1)) != HAL_OK) + return printf("hal_ecdsa_verify(public) failed: %s\n", hal_error_string(err)), 0; + + return 1; +} + +#endif /* HAL_ECDSA_DEBUG_ONLY_STATIC_TEST_VECTOR_RANDOM */ + +/* + * Run one keygen/sign/verify test with a newly generated key. + */ + +static int test_keygen_sign_verify(const hal_ecdsa_curve_t curve) + +{ + const hal_hash_descriptor_t *hash_descriptor = NULL; + uint8_t keybuf[hal_ecdsa_key_t_size]; + hal_ecdsa_key_t *key = NULL; + hal_error_t err; + + switch (curve) { + + case HAL_ECDSA_CURVE_P256: + printf("ECDSA P-256 key generation / signature / verification test\n"); + hash_descriptor = hal_hash_sha256; + break; + + case HAL_ECDSA_CURVE_P384: + printf("ECDSA P-384 key generation / signature / verification test\n"); + hash_descriptor = hal_hash_sha384; + break; + + case HAL_ECDSA_CURVE_P521: + printf("ECDSA P-521 key generation / signature / verification test\n"); + hash_descriptor = hal_hash_sha512; + break; + + default: + printf("Unsupported ECDSA curve type\n"); + return 0; + } + + if ((err = hal_ecdsa_key_gen(&key, keybuf, sizeof(keybuf), curve)) != HAL_OK) + return printf("hal_ecdsa_key_gen() failed: %s\n", hal_error_string(err)), 0; + + uint8_t hashbuf[hash_descriptor->digest_length]; + + { + const uint8_t plaintext[] = "So long, and thanks..."; + uint8_t statebuf[hash_descriptor->hash_state_length]; + hal_hash_state_t *state = NULL; + + if ((err = hal_hash_initialize(hash_descriptor, &state, statebuf, sizeof(statebuf))) != HAL_OK || + (err = hal_hash_update(state, plaintext, strlen((const char *) plaintext))) != HAL_OK || + (err = hal_hash_finalize(state, hashbuf, sizeof(hashbuf))) != HAL_OK) + return printf("Couldn't hash plaintext: %s\n", hal_error_string(err)), 0; + } + + /* + * Lazy but probably-good-enough guess on signature size -- want + * explicit number in ecdsa_curve_t? + */ + uint8_t sigbuf[hash_descriptor->digest_length * 3]; + size_t siglen; + + if ((err = hal_ecdsa_sign(key, hashbuf, sizeof(hashbuf), + sigbuf, &siglen, sizeof(sigbuf), HAL_ECDSA_SIGNATURE_FORMAT_ASN1)) != HAL_OK) + return printf("hal_ecdsa_sign() failed: %s\n", hal_error_string(err)), 0; + + if ((err = hal_ecdsa_verify(key, hashbuf, sizeof(hashbuf), + sigbuf, siglen, HAL_ECDSA_SIGNATURE_FORMAT_ASN1)) != HAL_OK) + return printf("hal_ecdsa_verify() failed: %s\n", hal_error_string(err)), 0; + + return 1; +} + +/* + * Time a test. + */ + +static void _time_check(const struct timeval t0, const int ok) +{ + struct timeval t; + gettimeofday(&t, NULL); + t.tv_sec -= t0.tv_sec; + t.tv_usec = t0.tv_usec; + if (t.tv_usec < 0) { + t.tv_usec += 1000000; + t.tv_sec -= 1; + } + printf("Elapsed time %lu.%06lu seconds, %s\n", + (unsigned long) t.tv_sec, + (unsigned long) t.tv_usec, + ok ? "OK" : "FAILED"); +} + +#define time_check(_expr_) \ + do { \ + struct timeval _t; \ + gettimeofday(&_t, NULL); \ + int _ok = (_expr_); \ + _time_check(_t, _ok); \ + ok &= _ok; \ + } while (0) + +/* + * Run tests for one ECDSA curve. + */ + +static int test_ecdsa(const ecdsa_tc_t * const tc) + +{ + int ok = 1; + time_check(test_against_static_vectors(tc)); + time_check(test_keygen_sign_verify(tc->curve)); + return ok; +} + +int main(int argc, char *argv[]) +{ + uint8_t name[8], version[4]; + hal_error_t err; + + /* + * Initialize EIM and report what core we're running. + */ + + if ((err = hal_io_read(CSPRNG_ADDR_NAME0, name, sizeof(name))) != HAL_OK || + (err = hal_io_read(CSPRNG_ADDR_VERSION, version, sizeof(version))) != HAL_OK) { + printf("Initialization failed: %s\n", hal_error_string(err)); + return 1; + } + + printf("\"%8.8s\" \"%4.4s\"\n\n", name, version); + + for (int i = 0; i < sizeof(ecdsa_tc)/sizeof(*ecdsa_tc); i++) + if (!test_ecdsa(&ecdsa_tc[i])) + return 1; + + return 0; +} + +/* + * Local variables: + * indent-tabs-mode: nil + * End: + */ diff --git a/tests/test-ecdsa.h b/tests/test-ecdsa.h new file mode 100644 index 0000000..ca51858 --- /dev/null +++ b/tests/test-ecdsa.h @@ -0,0 +1,329 @@ +/* + * ECDSA test data. + * File automatically generated by test-ecdsa.py + */ + +static const uint8_t p256_H[] = { /* 32 bytes */ + 0x7c, 0x3e, 0x88, 0x3d, 0xdc, 0x8b, 0xd6, 0x88, 0xf9, 0x6e, 0xac, 0x5e, + 0x93, 0x24, 0x22, 0x2c, 0x8f, 0x30, 0xf9, 0xd6, 0xbb, 0x59, 0xe9, 0xc5, + 0xf0, 0x20, 0xbd, 0x39, 0xba, 0x2b, 0x83, 0x77 +}; + +static const uint8_t p256_M[] = { /* 48 bytes */ + 0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x6f, 0x6e, 0x6c, 0x79, + 0x20, 0x61, 0x20, 0x74, 0x65, 0x73, 0x74, 0x20, 0x6d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x2e, 0x20, 0x49, 0x74, 0x20, 0x69, 0x73, 0x20, 0x34, + 0x38, 0x20, 0x62, 0x79, 0x74, 0x65, 0x73, 0x20, 0x6c, 0x6f, 0x6e, 0x67 +}; + +static const uint8_t p256_Qx[] = { /* 32 bytes */ + 0x81, 0x01, 0xec, 0xe4, 0x74, 0x64, 0xa6, 0xea, 0xd7, 0x0c, 0xf6, 0x9a, + 0x6e, 0x2b, 0xd3, 0xd8, 0x86, 0x91, 0xa3, 0x26, 0x2d, 0x22, 0xcb, 0xa4, + 0xf7, 0x63, 0x5e, 0xaf, 0xf2, 0x66, 0x80, 0xa8 +}; + +static const uint8_t p256_Qy[] = { /* 32 bytes */ + 0xd8, 0xa1, 0x2b, 0xa6, 0x1d, 0x59, 0x92, 0x35, 0xf6, 0x7d, 0x9c, 0xb4, + 0xd5, 0x8f, 0x17, 0x83, 0xd3, 0xca, 0x43, 0xe7, 0x8f, 0x0a, 0x5a, 0xba, + 0xa6, 0x24, 0x07, 0x99, 0x36, 0xc0, 0xc3, 0xa9 +}; + +static const uint8_t p256_Rx[] = { /* 32 bytes */ + 0x72, 0x14, 0xbc, 0x96, 0x47, 0x16, 0x0b, 0xbd, 0x39, 0xff, 0x2f, 0x80, + 0x53, 0x3f, 0x5d, 0xc6, 0xdd, 0xd7, 0x0d, 0xdf, 0x86, 0xbb, 0x81, 0x56, + 0x61, 0xe8, 0x05, 0xd5, 0xd4, 0xe6, 0xf2, 0x7c +}; + +static const uint8_t p256_Ry[] = { /* 32 bytes */ + 0x8b, 0x81, 0xe3, 0xe9, 0x77, 0x59, 0x71, 0x10, 0xc7, 0xcf, 0x26, 0x33, + 0x43, 0x5b, 0x22, 0x94, 0xb7, 0x26, 0x42, 0x98, 0x7d, 0xef, 0xd3, 0xd4, + 0x00, 0x7e, 0x1c, 0xfc, 0x5d, 0xf8, 0x45, 0x41 +}; + +static const uint8_t p256_d[] = { /* 32 bytes */ + 0x70, 0xa1, 0x2c, 0x2d, 0xb1, 0x68, 0x45, 0xed, 0x56, 0xff, 0x68, 0xcf, + 0xc2, 0x1a, 0x47, 0x2b, 0x3f, 0x04, 0xd7, 0xd6, 0x85, 0x1b, 0xf6, 0x34, + 0x9f, 0x2d, 0x7d, 0x5b, 0x34, 0x52, 0xb3, 0x8a +}; + +static const uint8_t p256_e[] = { /* 32 bytes */ + 0x7c, 0x3e, 0x88, 0x3d, 0xdc, 0x8b, 0xd6, 0x88, 0xf9, 0x6e, 0xac, 0x5e, + 0x93, 0x24, 0x22, 0x2c, 0x8f, 0x30, 0xf9, 0xd6, 0xbb, 0x59, 0xe9, 0xc5, + 0xf0, 0x20, 0xbd, 0x39, 0xba, 0x2b, 0x83, 0x77 +}; + +static const uint8_t p256_k[] = { /* 32 bytes */ + 0x58, 0x0e, 0xc0, 0x0d, 0x85, 0x64, 0x34, 0x33, 0x4c, 0xef, 0x3f, 0x71, + 0xec, 0xae, 0xd4, 0x96, 0x5b, 0x12, 0xae, 0x37, 0xfa, 0x47, 0x05, 0x5b, + 0x19, 0x65, 0xc7, 0xb1, 0x34, 0xee, 0x45, 0xd0 +}; + +static const uint8_t p256_key[] = { /* 121 bytes */ + 0x30, 0x77, 0x02, 0x01, 0x01, 0x04, 0x20, 0x70, 0xa1, 0x2c, 0x2d, 0xb1, + 0x68, 0x45, 0xed, 0x56, 0xff, 0x68, 0xcf, 0xc2, 0x1a, 0x47, 0x2b, 0x3f, + 0x04, 0xd7, 0xd6, 0x85, 0x1b, 0xf6, 0x34, 0x9f, 0x2d, 0x7d, 0x5b, 0x34, + 0x52, 0xb3, 0x8a, 0xa0, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, + 0x03, 0x01, 0x07, 0xa1, 0x44, 0x03, 0x42, 0x00, 0x04, 0x81, 0x01, 0xec, + 0xe4, 0x74, 0x64, 0xa6, 0xea, 0xd7, 0x0c, 0xf6, 0x9a, 0x6e, 0x2b, 0xd3, + 0xd8, 0x86, 0x91, 0xa3, 0x26, 0x2d, 0x22, 0xcb, 0xa4, 0xf7, 0x63, 0x5e, + 0xaf, 0xf2, 0x66, 0x80, 0xa8, 0xd8, 0xa1, 0x2b, 0xa6, 0x1d, 0x59, 0x92, + 0x35, 0xf6, 0x7d, 0x9c, 0xb4, 0xd5, 0x8f, 0x17, 0x83, 0xd3, 0xca, 0x43, + 0xe7, 0x8f, 0x0a, 0x5a, 0xba, 0xa6, 0x24, 0x07, 0x99, 0x36, 0xc0, 0xc3, 0xa9 +}; + +static const uint8_t p256_kinv[] = { /* 32 bytes */ + 0x6a, 0x66, 0x4f, 0xa1, 0x15, 0x35, 0x6d, 0x33, 0xf1, 0x63, 0x31, 0xb5, + 0x4c, 0x4e, 0x7c, 0xe9, 0x67, 0x96, 0x53, 0x86, 0xc7, 0xdc, 0xbf, 0x29, + 0x04, 0x60, 0x4d, 0x0c, 0x13, 0x2b, 0x4a, 0x74 +}; + +static const uint8_t p256_r[] = { /* 32 bytes */ + 0x72, 0x14, 0xbc, 0x96, 0x47, 0x16, 0x0b, 0xbd, 0x39, 0xff, 0x2f, 0x80, + 0x53, 0x3f, 0x5d, 0xc6, 0xdd, 0xd7, 0x0d, 0xdf, 0x86, 0xbb, 0x81, 0x56, + 0x61, 0xe8, 0x05, 0xd5, 0xd4, 0xe6, 0xf2, 0x7c +}; + +static const uint8_t p256_s[] = { /* 32 bytes */ + 0x7d, 0x1f, 0xf9, 0x61, 0x98, 0x0f, 0x96, 0x1b, 0xda, 0xa3, 0x23, 0x3b, + 0x62, 0x09, 0xf4, 0x01, 0x33, 0x17, 0xd3, 0xe3, 0xf9, 0xe1, 0x49, 0x35, + 0x92, 0xdb, 0xea, 0xa1, 0xaf, 0x2b, 0xc3, 0x67 +}; + +static const uint8_t p256_sig[] = { /* 70 bytes */ + 0x30, 0x44, 0x02, 0x20, 0x72, 0x14, 0xbc, 0x96, 0x47, 0x16, 0x0b, 0xbd, + 0x39, 0xff, 0x2f, 0x80, 0x53, 0x3f, 0x5d, 0xc6, 0xdd, 0xd7, 0x0d, 0xdf, + 0x86, 0xbb, 0x81, 0x56, 0x61, 0xe8, 0x05, 0xd5, 0xd4, 0xe6, 0xf2, 0x7c, + 0x02, 0x20, 0x7d, 0x1f, 0xf9, 0x61, 0x98, 0x0f, 0x96, 0x1b, 0xda, 0xa3, + 0x23, 0x3b, 0x62, 0x09, 0xf4, 0x01, 0x33, 0x17, 0xd3, 0xe3, 0xf9, 0xe1, + 0x49, 0x35, 0x92, 0xdb, 0xea, 0xa1, 0xaf, 0x2b, 0xc3, 0x67 +}; + +static const uint8_t p256_u1[] = { /* 32 bytes */ + 0xbb, 0x25, 0x24, 0x01, 0xd6, 0xfb, 0x32, 0x2b, 0xb7, 0x47, 0x18, 0x4c, + 0xf2, 0xac, 0x52, 0xbf, 0x8d, 0x54, 0xb9, 0x5a, 0x15, 0x15, 0x06, 0x2a, + 0x2f, 0x61, 0x41, 0xf2, 0xe2, 0x09, 0x2e, 0xd8 +}; + +static const uint8_t p256_u2[] = { /* 32 bytes */ + 0xaa, 0xe7, 0xd1, 0xc7, 0xf2, 0xc2, 0x32, 0xdf, 0xc6, 0x41, 0x94, 0x8a, + 0xf3, 0xdb, 0xa1, 0x41, 0xd4, 0xde, 0x86, 0x34, 0xe5, 0x71, 0xcf, 0x84, + 0xc4, 0x86, 0x30, 0x1b, 0x51, 0x0c, 0xfc, 0x04 +}; + +static const uint8_t p256_v[] = { /* 32 bytes */ + 0x72, 0x14, 0xbc, 0x96, 0x47, 0x16, 0x0b, 0xbd, 0x39, 0xff, 0x2f, 0x80, + 0x53, 0x3f, 0x5d, 0xc6, 0xdd, 0xd7, 0x0d, 0xdf, 0x86, 0xbb, 0x81, 0x56, + 0x61, 0xe8, 0x05, 0xd5, 0xd4, 0xe6, 0xf2, 0x7c +}; + +static const uint8_t p256_w[] = { /* 32 bytes */ + 0xd6, 0x9b, 0xe7, 0x5f, 0x67, 0xee, 0x53, 0x94, 0xca, 0xbb, 0x6c, 0x28, + 0x6f, 0x36, 0x10, 0xcf, 0x62, 0xd7, 0x22, 0xcb, 0xa9, 0xee, 0xa7, 0x0f, + 0xae, 0xe7, 0x70, 0xa6, 0xb2, 0xed, 0x72, 0xdc +}; + +static const uint8_t p384_H[] = { /* 48 bytes */ + 0xb9, 0x21, 0x0c, 0x9d, 0x7e, 0x20, 0x89, 0x7a, 0xb8, 0x65, 0x97, 0x26, + 0x6a, 0x9d, 0x50, 0x77, 0xe8, 0xdb, 0x1b, 0x06, 0xf7, 0x22, 0x0e, 0xd6, + 0xee, 0x75, 0xbd, 0x8b, 0x45, 0xdb, 0x37, 0x89, 0x1f, 0x8b, 0xa5, 0x55, + 0x03, 0x04, 0x00, 0x41, 0x59, 0xf4, 0x45, 0x3d, 0xc5, 0xb3, 0xf5, 0xa1 +}; + +static const uint8_t p384_M[] = { /* 48 bytes */ + 0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x6f, 0x6e, 0x6c, 0x79, + 0x20, 0x61, 0x20, 0x74, 0x65, 0x73, 0x74, 0x20, 0x6d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x2e, 0x20, 0x49, 0x74, 0x20, 0x69, 0x73, 0x20, 0x34, + 0x38, 0x20, 0x62, 0x79, 0x74, 0x65, 0x73, 0x20, 0x6c, 0x6f, 0x6e, 0x67 +}; + +static const uint8_t p384_Qx[] = { /* 48 bytes */ + 0x1f, 0xba, 0xc8, 0xee, 0xbd, 0x0c, 0xbf, 0x35, 0x64, 0x0b, 0x39, 0xef, + 0xe0, 0x80, 0x8d, 0xd7, 0x74, 0xde, 0xbf, 0xf2, 0x0a, 0x2a, 0x32, 0x9e, + 0x91, 0x71, 0x3b, 0xaf, 0x7d, 0x7f, 0x3c, 0x3e, 0x81, 0x54, 0x6d, 0x88, + 0x37, 0x30, 0xbe, 0xe7, 0xe4, 0x86, 0x78, 0xf8, 0x57, 0xb0, 0x2c, 0xa0 +}; + +static const uint8_t p384_Qy[] = { /* 48 bytes */ + 0xeb, 0x21, 0x31, 0x03, 0xbd, 0x68, 0xce, 0x34, 0x33, 0x65, 0xa8, 0xa4, + 0xc3, 0xd4, 0x55, 0x5f, 0xa3, 0x85, 0xf5, 0x33, 0x02, 0x03, 0xbd, 0xd7, + 0x6f, 0xfa, 0xd1, 0xf3, 0xaf, 0xfb, 0x95, 0x75, 0x1c, 0x13, 0x20, 0x07, + 0xe1, 0xb2, 0x40, 0x35, 0x3c, 0xb0, 0xa4, 0xcf, 0x16, 0x93, 0xbd, 0xf9 +}; + +static const uint8_t p384_Rx[] = { /* 48 bytes */ + 0xa0, 0xc2, 0x7e, 0xc8, 0x93, 0x09, 0x2d, 0xea, 0x1e, 0x1b, 0xd2, 0xcc, + 0xfe, 0xd3, 0xcf, 0x94, 0x5c, 0x81, 0x34, 0xed, 0x0c, 0x9f, 0x81, 0x31, + 0x1a, 0x0f, 0x4a, 0x05, 0x94, 0x2d, 0xb8, 0xdb, 0xed, 0x8d, 0xd5, 0x9f, + 0x26, 0x74, 0x71, 0xd5, 0x46, 0x2a, 0xa1, 0x4f, 0xe7, 0x2d, 0xe8, 0x56 +}; + +static const uint8_t p384_Ry[] = { /* 48 bytes */ + 0x85, 0x56, 0x49, 0x40, 0x98, 0x15, 0xbb, 0x91, 0x42, 0x4e, 0xac, 0xa5, + 0xfd, 0x76, 0xc9, 0x73, 0x75, 0xd5, 0x75, 0xd1, 0x42, 0x2e, 0xc5, 0x3d, + 0x34, 0x3b, 0xd3, 0x3b, 0x84, 0x7f, 0xdf, 0x0c, 0x11, 0x56, 0x96, 0x85, + 0xb5, 0x28, 0xab, 0x25, 0x49, 0x30, 0x15, 0x42, 0x8d, 0x7c, 0xf7, 0x2b +}; + +static const uint8_t p384_d[] = { /* 48 bytes */ + 0xc8, 0x38, 0xb8, 0x52, 0x53, 0xef, 0x8d, 0xc7, 0x39, 0x4f, 0xa5, 0x80, + 0x8a, 0x51, 0x83, 0x98, 0x1c, 0x7d, 0xee, 0xf5, 0xa6, 0x9b, 0xa8, 0xf4, + 0xf2, 0x11, 0x7f, 0xfe, 0xa3, 0x9c, 0xfc, 0xd9, 0x0e, 0x95, 0xf6, 0xcb, + 0xc8, 0x54, 0xab, 0xac, 0xab, 0x70, 0x1d, 0x50, 0xc1, 0xf3, 0xcf, 0x24 +}; + +static const uint8_t p384_e[] = { /* 48 bytes */ + 0xb9, 0x21, 0x0c, 0x9d, 0x7e, 0x20, 0x89, 0x7a, 0xb8, 0x65, 0x97, 0x26, + 0x6a, 0x9d, 0x50, 0x77, 0xe8, 0xdb, 0x1b, 0x06, 0xf7, 0x22, 0x0e, 0xd6, + 0xee, 0x75, 0xbd, 0x8b, 0x45, 0xdb, 0x37, 0x89, 0x1f, 0x8b, 0xa5, 0x55, + 0x03, 0x04, 0x00, 0x41, 0x59, 0xf4, 0x45, 0x3d, 0xc5, 0xb3, 0xf5, 0xa1 +}; + +static const uint8_t p384_k[] = { /* 48 bytes */ + 0xdc, 0x6b, 0x44, 0x03, 0x69, 0x89, 0xa1, 0x96, 0xe3, 0x9d, 0x1c, 0xda, + 0xc0, 0x00, 0x81, 0x2f, 0x4b, 0xdd, 0x8b, 0x2d, 0xb4, 0x1b, 0xb3, 0x3a, + 0xf5, 0x13, 0x72, 0x58, 0x5e, 0xbd, 0x1d, 0xb6, 0x3f, 0x0c, 0xe8, 0x27, + 0x5a, 0xa1, 0xfd, 0x45, 0xe2, 0xd2, 0xa7, 0x35, 0xf8, 0x74, 0x93, 0x59 +}; + +static const uint8_t p384_key[] = { /* 167 bytes */ + 0x30, 0x81, 0xa4, 0x02, 0x01, 0x01, 0x04, 0x30, 0xc8, 0x38, 0xb8, 0x52, + 0x53, 0xef, 0x8d, 0xc7, 0x39, 0x4f, 0xa5, 0x80, 0x8a, 0x51, 0x83, 0x98, + 0x1c, 0x7d, 0xee, 0xf5, 0xa6, 0x9b, 0xa8, 0xf4, 0xf2, 0x11, 0x7f, 0xfe, + 0xa3, 0x9c, 0xfc, 0xd9, 0x0e, 0x95, 0xf6, 0xcb, 0xc8, 0x54, 0xab, 0xac, + 0xab, 0x70, 0x1d, 0x50, 0xc1, 0xf3, 0xcf, 0x24, 0xa0, 0x07, 0x06, 0x05, + 0x2b, 0x81, 0x04, 0x00, 0x22, 0xa1, 0x64, 0x03, 0x62, 0x00, 0x04, 0x1f, + 0xba, 0xc8, 0xee, 0xbd, 0x0c, 0xbf, 0x35, 0x64, 0x0b, 0x39, 0xef, 0xe0, + 0x80, 0x8d, 0xd7, 0x74, 0xde, 0xbf, 0xf2, 0x0a, 0x2a, 0x32, 0x9e, 0x91, + 0x71, 0x3b, 0xaf, 0x7d, 0x7f, 0x3c, 0x3e, 0x81, 0x54, 0x6d, 0x88, 0x37, + 0x30, 0xbe, 0xe7, 0xe4, 0x86, 0x78, 0xf8, 0x57, 0xb0, 0x2c, 0xa0, 0xeb, + 0x21, 0x31, 0x03, 0xbd, 0x68, 0xce, 0x34, 0x33, 0x65, 0xa8, 0xa4, 0xc3, + 0xd4, 0x55, 0x5f, 0xa3, 0x85, 0xf5, 0x33, 0x02, 0x03, 0xbd, 0xd7, 0x6f, + 0xfa, 0xd1, 0xf3, 0xaf, 0xfb, 0x95, 0x75, 0x1c, 0x13, 0x20, 0x07, 0xe1, + 0xb2, 0x40, 0x35, 0x3c, 0xb0, 0xa4, 0xcf, 0x16, 0x93, 0xbd, 0xf9 +}; + +static const uint8_t p384_kinv[] = { /* 48 bytes */ + 0x74, 0x36, 0xf0, 0x30, 0x88, 0xe6, 0x5c, 0x37, 0xba, 0x8e, 0x7b, 0x33, + 0x88, 0x7f, 0xbc, 0x87, 0x75, 0x75, 0x14, 0xd6, 0x11, 0xf7, 0xd1, 0xfb, + 0xdf, 0x6d, 0x21, 0x04, 0xa2, 0x97, 0xad, 0x31, 0x8c, 0xdb, 0xf7, 0x40, + 0x4e, 0x4b, 0xa3, 0x7e, 0x59, 0x96, 0x66, 0xdf, 0x37, 0xb8, 0xd8, 0xbe +}; + +static const uint8_t p384_r[] = { /* 48 bytes */ + 0xa0, 0xc2, 0x7e, 0xc8, 0x93, 0x09, 0x2d, 0xea, 0x1e, 0x1b, 0xd2, 0xcc, + 0xfe, 0xd3, 0xcf, 0x94, 0x5c, 0x81, 0x34, 0xed, 0x0c, 0x9f, 0x81, 0x31, + 0x1a, 0x0f, 0x4a, 0x05, 0x94, 0x2d, 0xb8, 0xdb, 0xed, 0x8d, 0xd5, 0x9f, + 0x26, 0x74, 0x71, 0xd5, 0x46, 0x2a, 0xa1, 0x4f, 0xe7, 0x2d, 0xe8, 0x56 +}; + +static const uint8_t p384_s[] = { /* 48 bytes */ + 0x20, 0xab, 0x3f, 0x45, 0xb7, 0x4f, 0x10, 0xb6, 0xe1, 0x1f, 0x96, 0xa2, + 0xc8, 0xeb, 0x69, 0x4d, 0x20, 0x6b, 0x9d, 0xda, 0x86, 0xd3, 0xc7, 0xe3, + 0x31, 0xc2, 0x6b, 0x22, 0xc9, 0x87, 0xb7, 0x53, 0x77, 0x26, 0x57, 0x76, + 0x67, 0xad, 0xad, 0xf1, 0x68, 0xeb, 0xbe, 0x80, 0x37, 0x94, 0xa4, 0x02 +}; + +static const uint8_t p384_sig[] = { /* 103 bytes */ + 0x30, 0x65, 0x02, 0x31, 0x00, 0xa0, 0xc2, 0x7e, 0xc8, 0x93, 0x09, 0x2d, + 0xea, 0x1e, 0x1b, 0xd2, 0xcc, 0xfe, 0xd3, 0xcf, 0x94, 0x5c, 0x81, 0x34, + 0xed, 0x0c, 0x9f, 0x81, 0x31, 0x1a, 0x0f, 0x4a, 0x05, 0x94, 0x2d, 0xb8, + 0xdb, 0xed, 0x8d, 0xd5, 0x9f, 0x26, 0x74, 0x71, 0xd5, 0x46, 0x2a, 0xa1, + 0x4f, 0xe7, 0x2d, 0xe8, 0x56, 0x02, 0x30, 0x20, 0xab, 0x3f, 0x45, 0xb7, + 0x4f, 0x10, 0xb6, 0xe1, 0x1f, 0x96, 0xa2, 0xc8, 0xeb, 0x69, 0x4d, 0x20, + 0x6b, 0x9d, 0xda, 0x86, 0xd3, 0xc7, 0xe3, 0x31, 0xc2, 0x6b, 0x22, 0xc9, + 0x87, 0xb7, 0x53, 0x77, 0x26, 0x57, 0x76, 0x67, 0xad, 0xad, 0xf1, 0x68, + 0xeb, 0xbe, 0x80, 0x37, 0x94, 0xa4, 0x02 +}; + +static const uint8_t p384_u1[] = { /* 48 bytes */ + 0x6c, 0xe2, 0x56, 0x49, 0xd4, 0x2d, 0x22, 0x3e, 0x02, 0x0c, 0x11, 0x14, + 0x0f, 0xe7, 0x72, 0x32, 0x66, 0x12, 0xbb, 0x11, 0xb6, 0x86, 0xd3, 0x5e, + 0xe9, 0x8e, 0xd4, 0x55, 0x0e, 0x06, 0x35, 0xd9, 0xdd, 0x3a, 0x2a, 0xfb, + 0xca, 0x0c, 0xf2, 0xc4, 0xba, 0xed, 0xcd, 0x23, 0x31, 0x3b, 0x18, 0x9e +}; + +static const uint8_t p384_u2[] = { /* 48 bytes */ + 0xf3, 0xb2, 0x40, 0x75, 0x1d, 0x5d, 0x8e, 0xd3, 0x94, 0xa4, 0xb5, 0xbf, + 0x8e, 0x2a, 0x4c, 0x0e, 0x1e, 0x21, 0xaa, 0x51, 0xf2, 0x62, 0x0a, 0x08, + 0xb8, 0xc5, 0x5a, 0x2b, 0xc3, 0x34, 0xc9, 0x68, 0x99, 0x23, 0x16, 0x26, + 0x48, 0xf0, 0x6e, 0x5f, 0x46, 0x59, 0xfc, 0x52, 0x6d, 0x9c, 0x1f, 0xd6 +}; + +static const uint8_t p384_v[] = { /* 48 bytes */ + 0xa0, 0xc2, 0x7e, 0xc8, 0x93, 0x09, 0x2d, 0xea, 0x1e, 0x1b, 0xd2, 0xcc, + 0xfe, 0xd3, 0xcf, 0x94, 0x5c, 0x81, 0x34, 0xed, 0x0c, 0x9f, 0x81, 0x31, + 0x1a, 0x0f, 0x4a, 0x05, 0x94, 0x2d, 0xb8, 0xdb, 0xed, 0x8d, 0xd5, 0x9f, + 0x26, 0x74, 0x71, 0xd5, 0x46, 0x2a, 0xa1, 0x4f, 0xe7, 0x2d, 0xe8, 0x56 +}; + +static const uint8_t p384_w[] = { /* 48 bytes */ + 0x17, 0x98, 0x84, 0x5c, 0xd0, 0xa6, 0xce, 0xa5, 0x32, 0x7c, 0x50, 0x1a, + 0x71, 0xa4, 0xba, 0xf2, 0xf7, 0xbe, 0x88, 0x2c, 0xfb, 0xc3, 0x03, 0x75, + 0x0a, 0x7c, 0x86, 0x1a, 0xf8, 0xfe, 0x82, 0x25, 0x46, 0x7a, 0x25, 0x7f, + 0x5b, 0xf9, 0x1a, 0x4a, 0xaa, 0x5a, 0x79, 0xa8, 0x63, 0x7d, 0x21, 0x8a +}; + +typedef struct { + hal_ecdsa_curve_t curve; + const uint8_t * H; size_t H_len; + const uint8_t * M; size_t M_len; + const uint8_t * Qx; size_t Qx_len; + const uint8_t * Qy; size_t Qy_len; + const uint8_t * Rx; size_t Rx_len; + const uint8_t * Ry; size_t Ry_len; + const uint8_t * d; size_t d_len; + const uint8_t * e; size_t e_len; + const uint8_t * k; size_t k_len; + const uint8_t * key; size_t key_len; + const uint8_t * kinv; size_t kinv_len; + const uint8_t * r; size_t r_len; + const uint8_t * s; size_t s_len; + const uint8_t * sig; size_t sig_len; + const uint8_t * u1; size_t u1_len; + const uint8_t * u2; size_t u2_len; + const uint8_t * v; size_t v_len; + const uint8_t * w; size_t w_len; +} ecdsa_tc_t; + +static const ecdsa_tc_t ecdsa_tc[] = { + { HAL_ECDSA_CURVE_P256, + p256_H, sizeof(p256_H), + p256_M, sizeof(p256_M), + p256_Qx, sizeof(p256_Qx), + p256_Qy, sizeof(p256_Qy), + p256_Rx, sizeof(p256_Rx), + p256_Ry, sizeof(p256_Ry), + p256_d, sizeof(p256_d), + p256_e, sizeof(p256_e), + p256_k, sizeof(p256_k), + p256_key, sizeof(p256_key), + p256_kinv, sizeof(p256_kinv), + p256_r, sizeof(p256_r), + p256_s, sizeof(p256_s), + p256_sig, sizeof(p256_sig), + p256_u1, sizeof(p256_u1), + p256_u2, sizeof(p256_u2), + p256_v, sizeof(p256_v), + p256_w, sizeof(p256_w), + }, + { HAL_ECDSA_CURVE_P384, + p384_H, sizeof(p384_H), + p384_M, sizeof(p384_M), + p384_Qx, sizeof(p384_Qx), + p384_Qy, sizeof(p384_Qy), + p384_Rx, sizeof(p384_Rx), + p384_Ry, sizeof(p384_Ry), + p384_d, sizeof(p384_d), + p384_e, sizeof(p384_e), + p384_k, sizeof(p384_k), + p384_key, sizeof(p384_key), + p384_kinv, sizeof(p384_kinv), + p384_r, sizeof(p384_r), + p384_s, sizeof(p384_s), + p384_sig, sizeof(p384_sig), + p384_u1, sizeof(p384_u1), + p384_u2, sizeof(p384_u2), + p384_v, sizeof(p384_v), + p384_w, sizeof(p384_w), + }, +}; diff --git a/tests/test-ecdsa.py b/tests/test-ecdsa.py new file mode 100644 index 0000000..1ecfef9 --- /dev/null +++ b/tests/test-ecdsa.py @@ -0,0 +1,156 @@ +# Test vectors from "Suite B Implementer's Guide to FIPS 186-3". +# +# e is given in decimal, all other values are hex, because that's how +# these were given in the paper + +p256_d = 0x70a12c2db16845ed56ff68cfc21a472b3f04d7d6851bf6349f2d7d5b3452b38a +p256_Qx = 0x8101ece47464a6ead70cf69a6e2bd3d88691a3262d22cba4f7635eaff26680a8 +p256_Qy = 0xd8a12ba61d599235f67d9cb4d58f1783d3ca43e78f0a5abaa624079936c0c3a9 +p256_k = 0x580ec00d856434334cef3f71ecaed4965b12ae37fa47055b1965c7b134ee45d0 +p256_kinv = 0x6a664fa115356d33f16331b54c4e7ce967965386c7dcbf2904604d0c132b4a74 +p256_Rx = 0x7214bc9647160bbd39ff2f80533f5dc6ddd70ddf86bb815661e805d5d4e6f27c +p256_Ry = 0x8b81e3e977597110c7cf2633435b2294b72642987defd3d4007e1cfc5df84541 +p256_r = p256_Rx +p256_M = 0x54686973206973206f6e6c7920612074657374206d6573736167652e204974206973203438206279746573206c6f6e67 +p256_H = 0x7c3e883ddc8bd688f96eac5e9324222c8f30f9d6bb59e9c5f020bd39ba2b8377 +p256_e = 56197278047627432394583341962843287937266210957576322469816113796290471232375 +p256_s = 0x7d1ff961980f961bdaa3233b6209f4013317d3e3f9e1493592dbeaa1af2bc367 +p256_w = 0xd69be75f67ee5394cabb6c286f3610cf62d722cba9eea70faee770a6b2ed72dc +p256_u1 = 0xbb252401d6fb322bb747184cf2ac52bf8d54b95a1515062a2f6141f2e2092ed8 +p256_u2 = 0xaae7d1c7f2c232dfc641948af3dba141d4de8634e571cf84c486301b510cfc04 +p256_v = 0x7214bc9647160bbd39ff2f80533f5dc6ddd70ddf86bb815661e805d5d4e6f27c + +p384_d = 0xc838b85253ef8dc7394fa5808a5183981c7deef5a69ba8f4f2117ffea39cfcd90e95f6cbc854abacab701d50c1f3cf24 +p384_Qx = 0x1fbac8eebd0cbf35640b39efe0808dd774debff20a2a329e91713baf7d7f3c3e81546d883730bee7e48678f857b02ca0 +p384_Qy = 0xeb213103bd68ce343365a8a4c3d4555fa385f5330203bdd76ffad1f3affb95751c132007e1b240353cb0a4cf1693bdf9 +p384_k = 0xdc6b44036989a196e39d1cdac000812f4bdd8b2db41bb33af51372585ebd1db63f0ce8275aa1fd45e2d2a735f8749359 +p384_kinv = 0x7436f03088e65c37ba8e7b33887fbc87757514d611f7d1fbdf6d2104a297ad318cdbf7404e4ba37e599666df37b8d8be +p384_Rx = 0xa0c27ec893092dea1e1bd2ccfed3cf945c8134ed0c9f81311a0f4a05942db8dbed8dd59f267471d5462aa14fe72de856 +p384_Ry = 0x855649409815bb91424eaca5fd76c97375d575d1422ec53d343bd33b847fdf0c11569685b528ab25493015428d7cf72b +p384_r = p384_Rx +p384_M = 0x54686973206973206f6e6c7920612074657374206d6573736167652e204974206973203438206279746573206c6f6e67 +p384_H = 0xb9210c9d7e20897ab86597266a9d5077e8db1b06f7220ed6ee75bd8b45db37891f8ba5550304004159f4453dc5b3f5a1 +p384_e = 28493976155450475404302482243066463769180620629462008675793884393889401828800663731864240088367206094074919580333473 +p384_s = 0x20ab3f45b74f10b6e11f96a2c8eb694d206b9dda86d3c7e331c26b22c987b7537726577667adadf168ebbe803794a402 +p384_w = 0x1798845cd0a6cea5327c501a71a4baf2f7be882cfbc303750a7c861af8fe8225467a257f5bf91a4aaa5a79a8637d218a +p384_u1 = 0x6ce25649d42d223e020c11140fe772326612bb11b686d35ee98ed4550e0635d9dd3a2afbca0cf2c4baedcd23313b189e +p384_u2 = 0xf3b240751d5d8ed394a4b5bf8e2a4c0e1e21aa51f2620a08b8c55a2bc334c9689923162648f06e5f4659fc526d9c1fd6 +p384_v = 0xa0c27ec893092dea1e1bd2ccfed3cf945c8134ed0c9f81311a0f4a05942db8dbed8dd59f267471d5462aa14fe72de856 + +from textwrap import TextWrapper +from os.path import basename +from sys import argv +from pyasn1.type.univ import Sequence, Choice, Integer, OctetString, ObjectIdentifier, BitString +from pyasn1.type.namedtype import NamedTypes, NamedType, OptionalNamedType +from pyasn1.type.namedval import NamedValues +from pyasn1.type.tag import Tag, tagClassContext, tagFormatSimple +from pyasn1.type.constraint import SingleValueConstraint +from pyasn1.codec.der.encoder import encode as DER_Encode +from pyasn1.codec.der.decoder import decode as DER_Decode + +wrapper = TextWrapper(width = 78, initial_indent = " " * 2, subsequent_indent = " " * 2) + +def long_to_bytes(l): + # + # This is just plain nasty. + # + s = "%x" % l + return ("0" + s if len(s) & 1 else s).decode("hex") + +def bytes_to_bits(b): + # + # This, on the other hand, is not just plain nasty, this is fancy nasty. + # This is nasty with raisins in it. + # + bits = bin(long(b.encode("hex"), 16))[2:] + if len(bits) % 8: + bits = ("0" * (8 - len(bits) % 8)) + bits + return tuple(int(i) for i in bits) + +### + +class ECDSA_Sig_Value(Sequence): + componentType = NamedTypes( + NamedType("r", Integer()), + NamedType("s", Integer())) + +def encode_sig(r, s): + sig = ECDSA_Sig_Value() + sig["r"] = r + sig["s"] = s + return DER_Encode(sig) + +p256_sig = encode_sig(p256_r, p256_s) +p384_sig = encode_sig(p384_r, p384_s) + +### + +class ECPrivateKey(Sequence): + componentType = NamedTypes( + NamedType("version", Integer(namedValues = NamedValues(("ecPrivkeyVer1", 1)) + ).subtype(subtypeSpec = Integer.subtypeSpec + SingleValueConstraint(1))), + NamedType("privateKey", OctetString()), + OptionalNamedType("parameters", ObjectIdentifier().subtype(explicitTag = Tag(tagClassContext, tagFormatSimple, 0))), + OptionalNamedType("publicKey", BitString().subtype(explicitTag = Tag(tagClassContext, tagFormatSimple, 1)))) + +def encode_key(d, Qx, Qy, oid): + private_key = long_to_bytes(d) + public_key = bytes_to_bits(chr(0x04) + long_to_bytes(Qx) + long_to_bytes(Qy)) + parameters = oid + key = ECPrivateKey() + key["version"] = 1 + key["privateKey"] = private_key + key["parameters"] = parameters + key["publicKey"] = public_key + return DER_Encode(key) + +p256_key = encode_key(p256_d, p256_Qx, p256_Qy, "1.2.840.10045.3.1.7") +p384_key = encode_key(p384_d, p384_Qx, p384_Qy, "1.3.132.0.34") + +### + +print "/*" +print " * ECDSA test data." +print " * File automatically generated by", basename(argv[0]) +print " */" + +curves = ("p256", "p384") +vars = set() + +for name in dir(): + head, sep, tail = name.partition("_") + if head in curves: + vars.add(tail) + +vars = sorted(vars) + +for curve in curves: + for var in vars: + name = curve + "_" + var + value = globals().get(name, None) + if isinstance(value, (int, long)): + value = long_to_bytes(value) + if value is not None: + print + print "static const uint8_t %s[] = { /* %d bytes */" % (name, len(value)) + print wrapper.fill(", ".join("0x%02x" % ord(v) for v in value)) + print "};" + +print +print "typedef struct {" +print " hal_ecdsa_curve_t curve;" +for var in vars: + print " const uint8_t *%8s; size_t %8s_len;" % (var, var) +print "} ecdsa_tc_t;" +print +print "static const ecdsa_tc_t ecdsa_tc[] = {" +for curve in curves: + print " { HAL_ECDSA_CURVE_%s," % curve.upper() + for var in vars: + name = curve + "_" + var + if name in globals(): + print " %-14s sizeof(%s)," % (name + ",", name) + else: + print " %-14s 0," % "NULL," + print " }," +print "};" diff --git a/tests/test-hash.c b/tests/test-hash.c index befdf02..144b1b9 100644 --- a/tests/test-hash.c +++ b/tests/test-hash.c @@ -533,7 +533,7 @@ static int _test_hash(const hal_hash_descriptor_t * const descriptor, const char * const label) { uint8_t statebuf[512], digest[512]; - hal_hash_state_t state; + hal_hash_state_t *state = NULL; hal_error_t err; assert(descriptor != NULL && data != NULL && result != NULL && label != NULL); @@ -597,7 +597,7 @@ static int _test_hmac(const hal_hash_descriptor_t * const descriptor, const char * const label) { uint8_t statebuf[1024], digest[512]; - hal_hmac_state_t state; + hal_hmac_state_t *state = NULL; hal_error_t err; assert(descriptor != NULL && data != NULL && result != NULL && label != NULL); diff --git a/tests/test-pbkdf2.c b/tests/test-pbkdf2.c index 469b599..0688226 100644 --- a/tests/test-pbkdf2.c +++ b/tests/test-pbkdf2.c @@ -163,7 +163,7 @@ static int _test_pbkdf2(const uint8_t * const pwd, const size_t pwd_len, const uint8_t * const dk, const size_t dk_len, const unsigned count, const char * const label) { - printf("Starting test case %s\n", label); + printf("Starting PBKDF2 test case %s\n", label); uint8_t result[dk_len]; diff --git a/tests/test-rsa.c b/tests/test-rsa.c index f6bf55c..46afa03 100644 --- a/tests/test-rsa.c +++ b/tests/test-rsa.c @@ -88,7 +88,7 @@ static int test_decrypt(const char * const kind, const rsa_tc_t * const tc) printf("%s test for %lu-bit RSA key\n", kind, (unsigned long) tc->size); uint8_t keybuf[hal_rsa_key_t_size]; - hal_rsa_key_t key = { NULL }; + hal_rsa_key_t *key = NULL; hal_error_t err = HAL_OK; if ((err = hal_rsa_key_load_private(&key, @@ -130,7 +130,7 @@ static int test_gen(const char * const kind, const rsa_tc_t * const tc) char fn[sizeof("test-rsa-key-xxxxxx.der")]; uint8_t keybuf1[hal_rsa_key_t_size], keybuf2[hal_rsa_key_t_size]; - hal_rsa_key_t key1 = { NULL }, key2 = { NULL }; + hal_rsa_key_t *key1 = NULL, *key2 = NULL; hal_error_t err = HAL_OK; FILE *f; |