[crypto] Allocate FFDHE temporary space on demand

Now that key exchange algorithms are allowed to fail to construct a
shared public key, we can allocate the temporary working space for
FFDHE calculations on demand rather than using a static buffer.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
diff --git a/src/crypto/ffdhe.c b/src/crypto/ffdhe.c
index c47b03d..c11987b 100644
--- a/src/crypto/ffdhe.c
+++ b/src/crypto/ffdhe.c
@@ -47,6 +47,7 @@
 
 #include <assert.h>
 #include <stdint.h>
+#include <stdlib.h>
 #include <string.h>
 #include <errno.h>
 #include <ipxe/ffdhe.h>
@@ -165,27 +166,6 @@
 	0x90, 0xa6, 0xc0, 0x8f, 0x4d, 0xf4, 0x35, 0xc9
 };
 
-/** Maximum number of elements in big integer values */
-#define FFDHE_SIZE bigint_required_size ( FFDHE_LEN )
-
-/** Maximally sized big integer */
-typedef bigint_t ( FFDHE_SIZE ) ffdhe_t;
-
-/** Temporary storage */
-static struct {
-	/** Prime modulus */
-	ffdhe_t modulus;
-	/** Base */
-	ffdhe_t base;
-	/** Result */
-	ffdhe_t result;
-	/** Temporary working space */
-	union {
-		uint8_t mod_exp[ bigint_mod_exp_tmp_len ( (ffdhe_t *) NULL ) ];
-		uint8_t raw[FFDHE_LEN];
-	} tmp;
-} ffdhe_temp;
-
 /**
  * Calculate FFDHE result
  *
@@ -201,23 +181,41 @@
 	unsigned int size = group->size;
 	size_t explen = group->explen;
 	size_t len = group->len;
-	ffdhe_modulus_t ( len ) *tmp = ( ( void * ) &ffdhe_temp.tmp );
-	bigint_t ( size ) *modulus = ( ( void * ) &ffdhe_temp.modulus );
-	bigint_t ( size ) *base = ( ( void * ) &ffdhe_temp.base );
-	bigint_t ( size ) *result = ( ( void * ) &ffdhe_temp.result );
 	bigint_t ( expsize ) exponent;
+	bigint_t ( size ) *modulus;
+	bigint_t ( size ) *base;
+	bigint_t ( size ) *result;
+	ffdhe_modulus_t ( len ) *raw;
+	struct {
+		typeof ( *modulus ) modulus;
+		typeof ( *base ) base;
+		typeof ( *result ) result;
+		uint8_t mod_exp[ bigint_mod_exp_tmp_len ( modulus ) ];
+	} *tmp;
 	static const uint8_t two[1] = { 2 };
+	int rc;
 
 	/* Sanity checks */
 	static_assert ( sizeof ( euler ) == FFDHE_CONSTANT_LEN );
 	static_assert ( sizeof ( pi ) == FFDHE_CONSTANT_LEN );
 
-	/* Construct modulus */
-	assert ( sizeof ( *tmp ) == len );
-	memset ( tmp, 0xff, sizeof ( *tmp ) );
-	memcpy ( tmp->constant, group->constant, sizeof ( tmp->constant ) );
-	tmp->lsb32 = group->lsb32;
-	bigint_init ( modulus, tmp, len );
+	/* Allocate temporary space */
+	tmp = malloc ( sizeof ( *tmp ) );
+	if ( ! tmp ) {
+		rc = -ENOMEM;
+		goto err_alloc;
+	}
+	modulus = &tmp->modulus;
+	base = &tmp->base;
+	result = &tmp->result;
+
+	/* Construct modulus (using base as temporary buffer) */
+	assert ( sizeof ( *raw ) <= sizeof ( *base ) );
+	raw = ( ( void * ) base );
+	memset ( raw, 0xff, sizeof ( *raw ) );
+	memcpy ( raw->constant, group->constant, sizeof ( raw->constant ) );
+	raw->lsb32 = group->lsb32;
+	bigint_init ( modulus, raw, len );
 	DBGC ( group, "FFDHE %s mod: %s\n",
 	       group->name, bigint_ntoa ( modulus ) );
 
@@ -236,7 +234,7 @@
 	       group->name, bigint_ntoa ( &exponent ) );
 
 	/* Calculate result */
-	bigint_mod_exp ( base, modulus, &exponent, result, &ffdhe_temp.tmp );
+	bigint_mod_exp ( base, modulus, &exponent, result, tmp->mod_exp );
 	DBGC ( group, "FFDHE %s %s: %s\n", group->name,
 	       ( public ? "shr" : "pub" ), bigint_ntoa ( result ) );
 	bigint_done ( result, shared, len );
@@ -246,16 +244,24 @@
 	if ( ! bigint_is_geq ( result, base ) ) {
 		/* Result is 0 or 1 */
 		DBGC ( group, "FFDHE %s invalid result\n", group->name );
-		return -EPERM;
+		rc = -EPERM;
+		goto err_result;
 	}
 	bigint_add ( base, result );
 	if ( ! bigint_is_geq ( modulus, result ) ) {
 		/* Result is p-1 */
 		DBGC ( group, "FFDHE %s invalid result\n", group->name );
-		return -EPERM;
+		rc = -EPERM;
+		goto err_result;
 	}
 
-	return 0;
+	/* Success */
+	rc = 0;
+
+ err_result:
+	free ( tmp );
+ err_alloc:
+	return rc;
 }
 
 /**