[crypto] Generalise notion of uncompressed elliptic curve points

With algorithm private data pointers now available, the general
mechanism for key exchange using uncompressed elliptic curve points
can be separated from the Weierstrass curve implementation.

Generalise the mechanism for performing elliptic curve key exchange
using uncompressed affine co-ordinates.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
diff --git a/src/crypto/elliptic.c b/src/crypto/elliptic.c
new file mode 100644
index 0000000..75b8ba0
--- /dev/null
+++ b/src/crypto/elliptic.c
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2026 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * 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_SECBOOT ( PERMITTED );
+
+/** @file
+ *
+ * Elliptic curves
+ *
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <string.h>
+#include <ipxe/crypto.h>
+#include <ipxe/elliptic.h>
+
+/**
+ * Share public key
+ *
+ * @v exchange		Key exchange algorithm
+ * @v private		Private key
+ * @v public		Public key to fill in
+ */
+void elliptic_share ( struct exchange_algorithm *exchange, const void *private,
+		      void *public ) {
+	struct elliptic_curve *curve = exchange->priv;
+	size_t len = exchange->sharedsize;
+	elliptic_uncompressed_t ( len ) *result = public;
+	int rc;
+
+	/* Sanity checks */
+	assert ( curve->keysize == exchange->privsize );
+	assert ( curve->pointsize == sizeof ( result->xy ) );
+	assert ( sizeof ( *result ) == exchange->pubsize );
+
+	/* Calculate public key */
+	result->format = ELLIPTIC_FORMAT_UNCOMPRESSED;
+	rc = elliptic_multiply ( curve, curve->base, private, &result->xy );
+
+	/* Can never fail when using the curve's own base point */
+	assert ( rc == 0 );
+}
+
+/**
+ * Agree shared secret
+ *
+ * @v exchange		Key exchange algorithm
+ * @v private		Private key
+ * @v partner		Partner public key
+ * @v shared		Shared secret to fill in
+ * @ret rc		Return status code
+ */
+int elliptic_agree ( struct exchange_algorithm *exchange, const void *private,
+		     const void *partner, void *shared ) {
+	struct elliptic_curve *curve = exchange->priv;
+	size_t len = exchange->sharedsize;
+	const elliptic_uncompressed_t ( len ) *base = partner;
+	elliptic_uncompressed_t ( len ) result;
+	int rc;
+
+	/* Sanity checks */
+	assert ( curve->keysize == exchange->privsize );
+	assert ( curve->pointsize == sizeof ( base->xy ) );
+	assert ( curve->pointsize == sizeof ( result.xy ) );
+	assert ( sizeof ( *base ) == exchange->pubsize );
+	assert ( sizeof ( result.x ) == exchange->sharedsize );
+
+	/* Calculate shared secret */
+	if ( ( rc = elliptic_multiply ( curve, &base->xy, private,
+					result.xy ) ) != 0 )
+		return rc;
+	memcpy ( shared, &result.x, sizeof ( result.x ) );
+
+	/* Check for point at infinity */
+	if ( elliptic_is_infinity ( curve, &result.xy ) )
+		return -EPERM;
+
+	/* Check format byte */
+	if ( base->format != ELLIPTIC_FORMAT_UNCOMPRESSED )
+		return -EPERM;
+
+	return 0;
+}
diff --git a/src/crypto/p256.c b/src/crypto/p256.c
index 074f3c7..e6ea902 100644
--- a/src/crypto/p256.c
+++ b/src/crypto/p256.c
@@ -71,5 +71,8 @@
 };
 
 /** P-256 elliptic curve */
-WEIERSTRASS_CURVE ( p256, p256_curve, p256_algorithm, P256_LEN,
+WEIERSTRASS_CURVE ( p256, p256_curve, P256_LEN,
 		    p256_prime, p256_a, p256_b, p256_base, p256_order );
+
+/** P-256 key exchange */
+ELLIPTIC_EXCHANGE ( p256, p256_algorithm, P256_LEN, &p256_curve );
diff --git a/src/crypto/p384.c b/src/crypto/p384.c
index fe49adc..d5cfa1c 100644
--- a/src/crypto/p384.c
+++ b/src/crypto/p384.c
@@ -82,5 +82,8 @@
 };
 
 /** P-384 elliptic curve */
-WEIERSTRASS_CURVE ( p384, p384_curve, p384_algorithm, P384_LEN,
+WEIERSTRASS_CURVE ( p384, p384_curve, P384_LEN,
 		    p384_prime, p384_a, p384_b, p384_base, p384_order );
+
+/** P-384 key exchange */
+ELLIPTIC_EXCHANGE ( p384, p384_algorithm, P384_LEN, &p384_curve );
diff --git a/src/crypto/weierstrass.c b/src/crypto/weierstrass.c
index d669b9a..1f84919 100644
--- a/src/crypto/weierstrass.c
+++ b/src/crypto/weierstrass.c
@@ -1038,63 +1038,3 @@
 
 	return 0;
 }
-
-/**
- * Share public key
- *
- * @v exchange		Key exchange algorithm
- * @v private		Private key
- * @v public		Public key to fill in
- */
-void weierstrass_share ( struct exchange_algorithm *exchange,
-			 const void *private, void *public ) {
-	struct elliptic_curve *curve = exchange->priv;
-	struct weierstrass_curve *weierstrass = curve->priv;
-	size_t len = weierstrass->len;
-	weierstrass_uncompressed_t ( len ) *uncompressed = public;
-	int rc;
-
-	/* Calculate public key */
-	uncompressed->format = WEIERSTRASS_FORMAT;
-	rc = weierstrass_multiply ( curve, weierstrass->base, private,
-				    &uncompressed->xy );
-
-	/* Can never fail when using the curve's own base point */
-	assert ( rc == 0 );
-}
-
-/**
- * Agree shared secret
- *
- * @v exchange		Key exchange algorithm
- * @v private		Private key
- * @v partner		Partner public key
- * @v shared		Shared secret to fill in
- * @ret rc		Return status code
- */
-int weierstrass_agree ( struct exchange_algorithm *exchange,
-			const void *private, const void *partner,
-			void *shared ) {
-	struct elliptic_curve *curve = exchange->priv;
-	struct weierstrass_curve *weierstrass = curve->priv;
-	size_t len = weierstrass->len;
-	const weierstrass_uncompressed_t ( len ) *uncompressed = partner;
-	weierstrass_raw_t ( len ) point;
-	int rc;
-
-	/* Calculate shared secret */
-	if ( ( rc = weierstrass_multiply ( curve, &uncompressed->xy, private,
-					   &point.xy ) ) != 0 )
-		return rc;
-	memcpy ( shared, &point.x, sizeof ( point.x ) );
-
-	/* Check for point at infinity */
-	if ( weierstrass_is_infinity ( curve, &point.xy ) )
-		return -EPERM;
-
-	/* Check format byte */
-	if ( uncompressed->format != WEIERSTRASS_FORMAT )
-		return -EPERM;
-
-	return 0;
-}
diff --git a/src/include/ipxe/elliptic.h b/src/include/ipxe/elliptic.h
new file mode 100644
index 0000000..ff31651
--- /dev/null
+++ b/src/include/ipxe/elliptic.h
@@ -0,0 +1,50 @@
+#ifndef _IPXE_ELLIPTIC_H
+#define _IPXE_ELLIPTIC_H
+
+/** @file
+ *
+ * Elliptic curves
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
+
+#include <stdint.h>
+#include <ipxe/crypto.h>
+
+/** An uncompressed elliptic curve point */
+#define elliptic_uncompressed_t( len )					\
+	struct {							\
+		uint8_t format;						\
+		union {							\
+			struct {					\
+				uint8_t x[ (len) ];			\
+				uint8_t y[ (len) ];			\
+			} __attribute__ (( packed ));			\
+			uint8_t xy[ (len) * 2 ];			\
+		};							\
+	} __attribute__ (( packed ))
+
+/** Format byte for uncompressed curve point representation */
+#define ELLIPTIC_FORMAT_UNCOMPRESSED 0x04
+
+extern void elliptic_share ( struct exchange_algorithm *exchange,
+			     const void *private, void *public );
+extern int elliptic_agree ( struct exchange_algorithm *exchange,
+			    const void *private, const void *partner,
+			    void *shared );
+
+/** Define an uncompressed elliptic curve point key exchange algorithm */
+#define ELLIPTIC_EXCHANGE( _name, _exchange, _len, _curve )		\
+	struct exchange_algorithm _exchange = {				\
+		.name = #_name,						\
+		.privsize = (_len),					\
+		.pubsize = sizeof ( elliptic_uncompressed_t (_len) ),	\
+		.sharedsize = (_len),					\
+		.share = elliptic_share,				\
+		.agree = elliptic_agree,				\
+		.priv = _curve,						\
+	}
+
+#endif /* _IPXE_ELLIPTIC_H */
diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h
index 1398346..9f46617 100644
--- a/src/include/ipxe/errfile.h
+++ b/src/include/ipxe/errfile.h
@@ -447,7 +447,7 @@
 #define ERRFILE_usb_settings	      ( ERRFILE_OTHER | 0x00650000 )
 #define ERRFILE_weierstrass	      ( ERRFILE_OTHER | 0x00660000 )
 #define ERRFILE_efi_cacert	      ( ERRFILE_OTHER | 0x00670000 )
-#define ERRFILE_ecdhe		      ( ERRFILE_OTHER | 0x00680000 )
+#define ERRFILE_elliptic	      ( ERRFILE_OTHER | 0x00680000 )
 #define ERRFILE_ecdsa		      ( ERRFILE_OTHER | 0x00690000 )
 #define ERRFILE_crypto_null	      ( ERRFILE_OTHER | 0x006a0000 )
 #define ERRFILE_ffdhe		      ( ERRFILE_OTHER | 0x006b0000 )
diff --git a/src/include/ipxe/weierstrass.h b/src/include/ipxe/weierstrass.h
index 9e9514d..005f713 100644
--- a/src/include/ipxe/weierstrass.h
+++ b/src/include/ipxe/weierstrass.h
@@ -12,6 +12,7 @@
 
 #include <ipxe/bigint.h>
 #include <ipxe/crypto.h>
+#include <ipxe/elliptic.h>
 
 /** Number of axes in Weierstrass curve point representation */
 #define WEIERSTRASS_AXES 2
@@ -67,37 +68,6 @@
 		bigint_t ( size * 3 ) all;				\
 	}
 
-/**
- * Define a Weierstrass raw affine co-ordinate type
- *
- * @v len		Length of scalar values
- * @ret type		Raw affine co-ordinate type
- */
-#define weierstrass_raw_t( len )					\
-	union {								\
-		uint8_t axis[2][ len ];					\
-		struct {						\
-			uint8_t x[ len ];				\
-			uint8_t y[ len ];				\
-		} __attribute__ (( packed ));				\
-		uint8_t xy[ len * 2 ];					\
-	}
-
-/**
- * Define a Weierstrass "uncompressed" affine co-ordinate type
- *
- * @v len		Length of scalar values
- * @ret type		Uncompressed affine co-ordinate type
- */
-#define weierstrass_uncompressed_t( len )				\
-	struct {							\
-		uint8_t format;						\
-		weierstrass_raw_t ( len );				\
-	} __attribute__ (( packed ))
-
-/** Format byte for Weierstrass curve point representation */
-#define WEIERSTRASS_FORMAT 0x04 /* "uncompressed" */
-
 /** Indexes for stored multiples of the field prime */
 enum weierstrass_multiple {
 	WEIERSTRASS_N = 0,
@@ -169,8 +139,8 @@
 			       void *shared );
 
 /** Define a Weierstrass curve */
-#define WEIERSTRASS_CURVE( _name, _curve, _exchange, _len, _prime,	\
-			   _a, _b, _base, _order )			\
+#define WEIERSTRASS_CURVE( _name, _curve, _len, _prime, _a, _b, _base,	\
+			   _order )					\
 	static bigint_t ( weierstrass_size(_len) )			\
 		_name ## _cache[WEIERSTRASS_NUM_CACHED];		\
 	static struct weierstrass_curve _name ## _weierstrass = {	\
@@ -193,7 +163,7 @@
 	};								\
 	struct elliptic_curve _curve = {				\
 		.name = #_name,						\
-		.pointsize = sizeof ( weierstrass_raw_t(_len) ),	\
+		.pointsize = ( (_len) * WEIERSTRASS_AXES ),		\
 		.keysize = (_len),					\
 		.base = (_base),					\
 		.order = (_order),					\
@@ -201,15 +171,6 @@
 		.multiply = weierstrass_multiply,			\
 		.add = weierstrass_add_once,				\
 		.priv = &_name ## _weierstrass,				\
-	};								\
-	struct exchange_algorithm _exchange = {				\
-		.name = #_name,						\
-		.privsize = (_len),					\
-		.pubsize = sizeof ( weierstrass_uncompressed_t(_len) ), \
-		.sharedsize = (_len),					\
-		.share = weierstrass_share,				\
-		.agree = weierstrass_agree,				\
-		.priv = &_curve,					\
 	}
 
 #endif /* _IPXE_WEIERSTRASS_H */