diff --git a/src/include/ipxe/crypto.h b/src/include/ipxe/crypto.h index f458d7f30..ad085533a 100644 --- a/src/include/ipxe/crypto.h +++ b/src/include/ipxe/crypto.h @@ -174,6 +174,35 @@ struct pubkey_algorithm { const struct asn1_cursor *public_key ); }; +/** A key exchange algorithm */ +struct exchange_algorithm { + /** Algorithm name */ + const char *name; + /** Private key size */ + size_t privsize; + /** Public key size */ + size_t pubsize; + /** Shared secret size */ + size_t sharedsize; + /** + * Calculate public key + * + * @v private Private key + * @v public Public key to fill in + */ + void ( * public ) ( const void *private, void *public ); + /** + * Calculate shared secret + * + * @v private Private key + * @v partner Partner public key + * @v shared Shared secret to fill in + * @ret rc Return status code + */ + int ( * shared ) ( const void *private, const void *partner, + void *shared ); +}; + /** An elliptic curve */ struct elliptic_curve { /** Curve name */ @@ -318,6 +347,18 @@ pubkey_match ( struct pubkey_algorithm *pubkey, return pubkey->match ( private_key, public_key ); } +static inline __attribute__ (( always_inline )) void +exchange_public ( struct exchange_algorithm *exchange, const void *private, + void *public ) { + exchange->public ( private, public ); +} + +static inline __attribute__ (( always_inline )) int +exchange_shared ( struct exchange_algorithm *exchange, const void *private, + const void *partner, void *shared ) { + return exchange->shared ( private, partner, shared ); +} + static inline __attribute__ (( always_inline )) int elliptic_is_infinity ( struct elliptic_curve *curve, const void *point ) { return curve->is_infinity ( point ); diff --git a/src/tests/exchange_test.c b/src/tests/exchange_test.c new file mode 100644 index 000000000..ab2d0c549 --- /dev/null +++ b/src/tests/exchange_test.c @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2026 Michael Brown . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** @file + * + * Key exchange self-tests + * + */ + +/* Forcibly enable assertions */ +#undef NDEBUG + +#include +#include +#include +#include "exchange_test.h" + +/** + * Report a key exchange test result + * + * @v test Key exchange test + * @v file Test code file + * @v line Test code line + */ +void exchange_okx ( struct exchange_test *test, const char *file, + unsigned int line ) { + struct exchange_algorithm *exchange = test->exchange; + uint8_t public[ exchange->pubsize ]; + uint8_t shared[ exchange->sharedsize ]; + int rc; + + /* Verify calculation of public key */ + DBGC ( test, "KEX %s private key:\n", exchange->name ); + DBGC_HDA ( test, 0, test->private, exchange->privsize ); + exchange_public ( exchange, test->private, public ); + DBGC ( test, "KEX %s public key:\n", exchange->name ); + DBGC_HDA ( test, 0, public, exchange->pubsize ); + okx ( memcmp ( public, test->public, exchange->pubsize ) == 0, + file, line ); + + /* Verify calculation of shared secret */ + DBGC ( test, "KEX %s partner key:\n", exchange->name ); + DBGC_HDA ( test, 0, test->partner, exchange->pubsize ); + rc = exchange_shared ( exchange, test->private, test->partner, shared ); + if ( test->shared ) { + /* Verify successful calculation */ + okx ( rc == 0, file, line ); + DBGC ( test, "KEX %s shared secret:\n", exchange->name ); + DBGC_HDA ( test, 0, shared, exchange->sharedsize ); + okx ( memcmp ( shared, test->shared, + exchange->sharedsize ) == 0, file, line ); + } else { + /* Verify failure */ + okx ( rc != 0, file, line ); + } +} diff --git a/src/tests/exchange_test.h b/src/tests/exchange_test.h new file mode 100644 index 000000000..464692c8a --- /dev/null +++ b/src/tests/exchange_test.h @@ -0,0 +1,75 @@ +#ifndef _EXCHANGE_TEST_H +#define _EXCHANGE_TEST_H + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include + +/** A key exchange test */ +struct exchange_test { + /** Key exchange algorithm */ + struct exchange_algorithm *exchange; + /** Private key */ + const void *private; + /** Partner public key */ + const void *partner; + /** Expected public key */ + const void *public; + /** Expected shared secret, or NULL to expect failure */ + const void *shared; +}; + +/** Define inline private key */ +#define PRIVATE(...) { __VA_ARGS__ } + +/** Define inline partner public key */ +#define PARTNER(...) { __VA_ARGS__ } + +/** Define inline expected public key */ +#define PUBLIC(...) { __VA_ARGS__ } + +/** Define inline expected shared secret */ +#define SHARED(...) { __VA_ARGS__ } + +/** Define inline expected failure result */ +#define SHARED_FAIL SHARED() + +/** + * Define a key exchange test + * + * @v name Test name + * @v EXCHANGE Key exchange algorithm + * @v PRIVATE Private key + * @v PARTNER Partner public key + * @v PUBLIC Expected public key + * @v SHARED Expected shared secret + * @ret test Key exchange test + */ +#define EXCHANGE_TEST( name, EXCHANGE, PRIVATE, PARTNER, PUBLIC, \ + SHARED ) \ + static const uint8_t name ## _private[] = PRIVATE; \ + static const uint8_t name ## _partner[] = PARTNER; \ + static const uint8_t name ## _public[] = PUBLIC; \ + static const uint8_t name ## _shared[] = SHARED; \ + static struct exchange_test name = { \ + .exchange = EXCHANGE, \ + .private = name ## _private, \ + .partner = name ## _partner, \ + .public = name ## _public, \ + .shared = ( sizeof ( name ## _shared ) ? \ + name ## _shared : NULL ), \ + }; + +/** + * Report a key exchange test result + * + * @v test Key exchange test + */ +#define exchange_ok(test) exchange_okx ( test, __FILE__, __LINE__ ) + +extern void exchange_okx ( struct exchange_test *test, const char *file, + unsigned int line ); + +#endif /* _EXCHANGE_TEST_H */