]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[crypto] Allow for addition of arbitrary Weierstrass curve points
authorMichael Brown <mcb30@ipxe.org>
Sat, 6 Dec 2025 16:59:29 +0000 (16:59 +0000)
committerMichael Brown <mcb30@ipxe.org>
Mon, 8 Dec 2025 14:24:24 +0000 (14:24 +0000)
ECDSA verification requires the ability to add two arbitrary curve
points (as well as the ability to multiply a curve point by a scalar).

Add an elliptic curve method to perform arbitrary point addition.
Pass in curve points as affine coordinates: this will require some
redundant conversions between affine coorfinates and the internal
representation as projective coordinates in Montgomery form, but keeps
the API as simple as possible.  Since we do not expect to perform a
high volume of ECDSA signature verifications, these redundant
calculations are an acceptable cost for keeping the code simple.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
src/crypto/weierstrass.c
src/crypto/x25519.c
src/include/ipxe/crypto.h
src/include/ipxe/weierstrass.h
src/tests/elliptic_test.c
src/tests/elliptic_test.h
src/tests/p256_test.c
src/tests/p384_test.c

index ecc9699a146eb339c28cefecdc185005e4319e99..b19970cef99f31526cd97ee4a5f96c13c747b452 100644 (file)
@@ -953,3 +953,44 @@ int weierstrass_multiply ( struct weierstrass_curve *curve, const void *base,
 
        return 0;
 }
+
+/**
+ * Add curve points (as a one-off operation)
+ *
+ * @v curve            Weierstrass curve
+ * @v addend           Curve point to add
+ * @v augend           Curve point to add
+ * @v result           Curve point to hold result
+ * @ret rc             Return status code
+ */
+int weierstrass_add_once ( struct weierstrass_curve *curve, const void *addend,
+                          const void *augend, void *result ) {
+       unsigned int size = curve->size;
+       struct {
+               weierstrass_t ( size ) addend;
+               weierstrass_t ( size ) augend;
+               weierstrass_t ( size ) result;
+       } temp;
+       int rc;
+
+       /* Convert inputs to projective coordinates in Montgomery form */
+       if ( ( rc = weierstrass_init ( curve, &temp.addend, &temp.result,
+                                      addend ) ) != 0 ) {
+               return rc;
+       }
+       if ( ( rc = weierstrass_init ( curve, &temp.augend, &temp.result,
+                                      augend ) ) != 0 ) {
+               return rc;
+       }
+
+       /* Add curve points */
+       weierstrass_add ( curve, &temp.augend, &temp.addend, &temp.result );
+
+       /* Convert result back to affine co-ordinates */
+       if ( ( rc = weierstrass_done ( curve, &temp.result, &temp.addend,
+                                      result ) ) != 0 ) {
+               return rc;
+       }
+
+       return 0;
+}
index 00a791877f65d61ae719b61546f8102b12f7390d..11094ba6e440dc2fbeaec588a1fbea16b75f1da5 100644 (file)
@@ -833,6 +833,21 @@ static int x25519_curve_multiply ( const void *base, const void *scalar,
        return x25519_key ( base, scalar, result );
 }
 
+/**
+ * Add curve points (as a one-off operation)
+ *
+ * @v addend           Curve point to add
+ * @v augend           Curve point to add
+ * @v result           Curve point to hold result
+ * @ret rc             Return status code
+ */
+static int x25519_curve_add ( const void *addend __unused,
+                             const void *augend __unused,
+                             void *result __unused ) {
+
+       return -ENOTTY;
+}
+
 /** X25519 elliptic curve */
 struct elliptic_curve x25519_curve = {
        .name = "x25519",
@@ -840,4 +855,5 @@ struct elliptic_curve x25519_curve = {
        .keysize = sizeof ( struct x25519_value ),
        .base = x25519_generator.raw,
        .multiply = x25519_curve_multiply,
+       .add = x25519_curve_add,
 };
index 8941ffa01e3380a9fd96d63698a0e0a6fec7015f..93b718e15a266004f1b00c93d3df425ca6f6c40c 100644 (file)
@@ -194,6 +194,14 @@ struct elliptic_curve {
         */
        int ( * multiply ) ( const void *base, const void *scalar,
                             void *result );
+       /** Add curve points (as a one-off operation)
+        *
+        * @v addend            Curve point to add
+        * @v augend            Curve point to add
+        * @v result            Curve point to hold result
+        * @ret rc              Return status code
+        */
+       int ( * add ) ( const void *addend, const void *augend, void *result );
 };
 
 static inline __attribute__ (( always_inline )) void
@@ -305,6 +313,12 @@ elliptic_multiply ( struct elliptic_curve *curve,
        return curve->multiply ( base, scalar, result );
 }
 
+static inline __attribute__ (( always_inline )) int
+elliptic_add ( struct elliptic_curve *curve, const void *addend,
+              const void *augend, void *result ) {
+       return curve->add ( addend, augend, result );
+}
+
 extern void digest_null_init ( void *ctx );
 extern void digest_null_update ( void *ctx, const void *src, size_t len );
 extern void digest_null_final ( void *ctx, void *out );
index b718886f6302acccabcd5c0d9aca9576aeed4970..ca3c216e62c8dcc7d5d9c6fd323320aeb0605f95 100644 (file)
@@ -126,6 +126,9 @@ struct weierstrass_curve {
 extern int weierstrass_multiply ( struct weierstrass_curve *curve,
                                  const void *base, const void *scalar,
                                  void *result );
+extern int weierstrass_add_once ( struct weierstrass_curve *curve,
+                                 const void *addend, const void *augend,
+                                 void *result );
 
 /** Define a Weierstrass curve */
 #define WEIERSTRASS_CURVE( _name, _curve, _len, _prime, _a, _b, _base, \
@@ -157,6 +160,11 @@ extern int weierstrass_multiply ( struct weierstrass_curve *curve,
                return weierstrass_multiply ( &_name ## _weierstrass,   \
                                              base, scalar, result );   \
        }                                                               \
+       static int _name ## _add ( const void *addend,                  \
+                                  const void *augend, void *result) {  \
+               return weierstrass_add_once ( &_name ## _weierstrass,   \
+                                             addend, augend, result ); \
+       }                                                               \
        struct elliptic_curve _curve = {                                \
                .name = #_name,                                         \
                .pointsize = ( WEIERSTRASS_AXES * (_len) ),             \
@@ -164,6 +172,7 @@ extern int weierstrass_multiply ( struct weierstrass_curve *curve,
                .base = (_base),                                        \
                .order = (_order),                                      \
                .multiply = _name ## _multiply,                         \
+               .add = _name ## _add,                                   \
        }
 
 #endif /* _IPXE_WEIERSTRASS_H */
index a2266626d4a7dd2cd91753e764f4f8a9ad7ab556..5b21ee00edce1b7b5e2e98038a8bc017f943f9ff 100644 (file)
@@ -118,3 +118,36 @@ void elliptic_multiply_okx ( struct elliptic_multiply_test *test,
        okx ( memcmp ( actual, test->expected, test->expected_len ) == 0,
              file, line );
 }
+
+/**
+ * Report elliptic curve point addition test result
+ *
+ * @v test             Elliptic curve point addition test
+ * @v file             Test code file
+ * @v line             Test code line
+ */
+void elliptic_add_okx ( struct elliptic_add_test *test,
+                       const char *file, unsigned int line ) {
+       struct elliptic_curve *curve = test->curve;
+       size_t pointsize = curve->pointsize;
+       uint8_t actual[pointsize];
+       int rc;
+
+       /* Sanity checks */
+       okx ( test->addend_len == pointsize, file, line );
+       okx ( test->augend_len == pointsize, file, line );
+       okx ( ( test->expected_len == pointsize ) || ( ! test->expected_len ),
+             file, line );
+
+       /* Perform point addition */
+       rc = elliptic_add ( curve, test->addend, test->augend, actual );
+       if ( test->expected_len ) {
+               okx ( rc == 0, file, line );
+       } else {
+               okx ( rc != 0, file, line );
+       }
+
+       /* Check expected result */
+       okx ( memcmp ( actual, test->expected, test->expected_len ) == 0,
+             file, line );
+}
index 1fcc4b108480d415fa191ce3df6807d044ccca0b..eab242f17a8bd37b0f266310f5b3ffc250ae3711 100644 (file)
@@ -25,6 +25,24 @@ struct elliptic_multiply_test {
        size_t expected_len;
 };
 
+/** An elliptic curve point addition test */
+struct elliptic_add_test {
+       /** Elliptic curve */
+       struct elliptic_curve *curve;
+       /** Addend point */
+       const void *addend;
+       /** Length of addend point */
+       size_t addend_len;
+       /** Augend point */
+       const void *augend;
+       /** Length of augend point */
+       size_t augend_len;
+       /** Expected result point */
+       const void *expected;
+       /** Length of expected result point (or 0 to expect failure) */
+       size_t expected_len;
+};
+
 /** Define inline base point */
 #define BASE(...) { __VA_ARGS__ }
 
@@ -34,6 +52,12 @@ struct elliptic_multiply_test {
 /** Define inline scalar multiple */
 #define SCALAR(...) { __VA_ARGS__ }
 
+/** Define inline addend point */
+#define ADDEND(...) { __VA_ARGS__ }
+
+/** Define inline augend point */
+#define AUGEND(...) { __VA_ARGS__ }
+
 /** Define inline expected result point */
 #define EXPECTED(...) { __VA_ARGS__ }
 
@@ -64,10 +88,36 @@ struct elliptic_multiply_test {
                .expected_len = sizeof ( name ## _expected ),           \
        };
 
+/**
+ * Define an elliptic curve point addition test
+ *
+ * @v name             Test name
+ * @v CURVE            Elliptic curve
+ * @v ADDEND           Addend point
+ * @v AUGEND           Augend point
+ * @v EXPECTED         Expected result point
+ * @ret test           Elliptic curve point multiplication test
+ */
+#define ELLIPTIC_ADD_TEST( name, CURVE, ADDEND, AUGEND, EXPECTED )     \
+       static const uint8_t name ## _addend[] = ADDEND;                \
+       static const uint8_t name ## _augend[] = AUGEND;                \
+       static const uint8_t name ## _expected[] = EXPECTED;            \
+       static struct elliptic_add_test name = {                        \
+               .curve = CURVE,                                         \
+               .addend = name ## _addend,                              \
+               .addend_len = sizeof ( name ## _addend ),               \
+               .augend = name ## _augend,                              \
+               .augend_len = sizeof ( name ## _augend ),               \
+               .expected = name ## _expected,                          \
+               .expected_len = sizeof ( name ## _expected ),           \
+       };
+
 extern void elliptic_curve_okx ( struct elliptic_curve *curve,
                                 const char *file, unsigned int line );
 extern void elliptic_multiply_okx ( struct elliptic_multiply_test *test,
                                    const char *file, unsigned int line );
+extern void elliptic_add_okx ( struct elliptic_add_test *test,
+                              const char *file, unsigned int line );
 
 /**
  * Report an elliptic curve sanity test result
@@ -85,4 +135,12 @@ extern void elliptic_multiply_okx ( struct elliptic_multiply_test *test,
 #define elliptic_multiply_ok( test ) \
        elliptic_multiply_okx ( test, __FILE__, __LINE__ )
 
+/**
+ * Report an elliptic curve point addition test result
+ *
+ * @v test             Elliptic curve point addition test
+ */
+#define elliptic_add_ok( test ) \
+       elliptic_add_okx ( test, __FILE__, __LINE__ )
+
 #endif /* _ELLIPTIC_TEST_H */
index 8b425f2154045d4c0458d94e7449f47f6a7b0391..b7bbe47b6cb161330eeddd30c81fadccc1ba6bbb 100644 (file)
@@ -151,6 +151,80 @@ ELLIPTIC_MULTIPLY_TEST ( invalid_one, &p256_curve,
                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 ),
        EXPECTED_FAIL );
 
+/* http://point-at-infinity.org/ecc/nisttv k=2 + k=2 => k=4 */
+ELLIPTIC_ADD_TEST ( poi_2_2_4, &p256_curve,
+       ADDEND ( 0x7c, 0xf2, 0x7b, 0x18, 0x8d, 0x03, 0x4f, 0x7e,
+                0x8a, 0x52, 0x38, 0x03, 0x04, 0xb5, 0x1a, 0xc3,
+                0xc0, 0x89, 0x69, 0xe2, 0x77, 0xf2, 0x1b, 0x35,
+                0xa6, 0x0b, 0x48, 0xfc, 0x47, 0x66, 0x99, 0x78,
+                0x07, 0x77, 0x55, 0x10, 0xdb, 0x8e, 0xd0, 0x40,
+                0x29, 0x3d, 0x9a, 0xc6, 0x9f, 0x74, 0x30, 0xdb,
+                0xba, 0x7d, 0xad, 0xe6, 0x3c, 0xe9, 0x82, 0x29,
+                0x9e, 0x04, 0xb7, 0x9d, 0x22, 0x78, 0x73, 0xd1 ),
+       AUGEND ( 0x7c, 0xf2, 0x7b, 0x18, 0x8d, 0x03, 0x4f, 0x7e,
+                0x8a, 0x52, 0x38, 0x03, 0x04, 0xb5, 0x1a, 0xc3,
+                0xc0, 0x89, 0x69, 0xe2, 0x77, 0xf2, 0x1b, 0x35,
+                0xa6, 0x0b, 0x48, 0xfc, 0x47, 0x66, 0x99, 0x78,
+                0x07, 0x77, 0x55, 0x10, 0xdb, 0x8e, 0xd0, 0x40,
+                0x29, 0x3d, 0x9a, 0xc6, 0x9f, 0x74, 0x30, 0xdb,
+                0xba, 0x7d, 0xad, 0xe6, 0x3c, 0xe9, 0x82, 0x29,
+                0x9e, 0x04, 0xb7, 0x9d, 0x22, 0x78, 0x73, 0xd1 ),
+       EXPECTED ( 0xe2, 0x53, 0x4a, 0x35, 0x32, 0xd0, 0x8f, 0xbb,
+                  0xa0, 0x2d, 0xde, 0x65, 0x9e, 0xe6, 0x2b, 0xd0,
+                  0x03, 0x1f, 0xe2, 0xdb, 0x78, 0x55, 0x96, 0xef,
+                  0x50, 0x93, 0x02, 0x44, 0x6b, 0x03, 0x08, 0x52,
+                  0xe0, 0xf1, 0x57, 0x5a, 0x4c, 0x63, 0x3c, 0xc7,
+                  0x19, 0xdf, 0xee, 0x5f, 0xda, 0x86, 0x2d, 0x76,
+                  0x4e, 0xfc, 0x96, 0xc3, 0xf3, 0x0e, 0xe0, 0x05,
+                  0x5c, 0x42, 0xc2, 0x3f, 0x18, 0x4e, 0xd8, 0xc6 ) );
+
+/* http://point-at-infinity.org/ecc/nisttv k=3 + k=5 => k=8 */
+ELLIPTIC_ADD_TEST ( poi_3_5_8, &p256_curve,
+       ADDEND ( 0x5e, 0xcb, 0xe4, 0xd1, 0xa6, 0x33, 0x0a, 0x44,
+                0xc8, 0xf7, 0xef, 0x95, 0x1d, 0x4b, 0xf1, 0x65,
+                0xe6, 0xc6, 0xb7, 0x21, 0xef, 0xad, 0xa9, 0x85,
+                0xfb, 0x41, 0x66, 0x1b, 0xc6, 0xe7, 0xfd, 0x6c,
+                0x87, 0x34, 0x64, 0x0c, 0x49, 0x98, 0xff, 0x7e,
+                0x37, 0x4b, 0x06, 0xce, 0x1a, 0x64, 0xa2, 0xec,
+                0xd8, 0x2a, 0xb0, 0x36, 0x38, 0x4f, 0xb8, 0x3d,
+                0x9a, 0x79, 0xb1, 0x27, 0xa2, 0x7d, 0x50, 0x32 ),
+       AUGEND ( 0x51, 0x59, 0x0b, 0x7a, 0x51, 0x51, 0x40, 0xd2,
+                0xd7, 0x84, 0xc8, 0x56, 0x08, 0x66, 0x8f, 0xdf,
+                0xef, 0x8c, 0x82, 0xfd, 0x1f, 0x5b, 0xe5, 0x24,
+                0x21, 0x55, 0x4a, 0x0d, 0xc3, 0xd0, 0x33, 0xed,
+                0xe0, 0xc1, 0x7d, 0xa8, 0x90, 0x4a, 0x72, 0x7d,
+                0x8a, 0xe1, 0xbf, 0x36, 0xbf, 0x8a, 0x79, 0x26,
+                0x0d, 0x01, 0x2f, 0x00, 0xd4, 0xd8, 0x08, 0x88,
+                0xd1, 0xd0, 0xbb, 0x44, 0xfd, 0xa1, 0x6d, 0xa4 ),
+       EXPECTED ( 0x62, 0xd9, 0x77, 0x9d, 0xbe, 0xe9, 0xb0, 0x53,
+                  0x40, 0x42, 0x74, 0x2d, 0x3a, 0xb5, 0x4c, 0xad,
+                  0xc1, 0xd2, 0x38, 0x98, 0x0f, 0xce, 0x97, 0xdb,
+                  0xb4, 0xdd, 0x9d, 0xc1, 0xdb, 0x6f, 0xb3, 0x93,
+                  0xad, 0x5a, 0xcc, 0xbd, 0x91, 0xe9, 0xd8, 0x24,
+                  0x4f, 0xf1, 0x5d, 0x77, 0x11, 0x67, 0xce, 0xe0,
+                  0xa2, 0xed, 0x51, 0xf6, 0xbb, 0xe7, 0x6a, 0x78,
+                  0xda, 0x54, 0x0a, 0x6a, 0x0f, 0x09, 0x95, 0x7e ) );
+
+/* http://point-at-infinity.org/ecc/nisttv k=1 + k=n-1 => infinity */
+ELLIPTIC_ADD_TEST ( poi_1_n_1, &p256_curve,
+       ADDEND ( 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,
+                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 ),
+       AUGEND ( 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,
+                0xb0, 0x1c, 0xbd, 0x1c, 0x01, 0xe5, 0x80, 0x65,
+                0x71, 0x18, 0x14, 0xb5, 0x83, 0xf0, 0x61, 0xe9,
+                0xd4, 0x31, 0xcc, 0xa9, 0x94, 0xce, 0xa1, 0x31,
+                0x34, 0x49, 0xbf, 0x97, 0xc8, 0x40, 0xae, 0x0a ),
+       EXPECTED_FAIL );
+
 /**
  * Perform P-256 self-test
  *
@@ -160,7 +234,7 @@ static void p256_test_exec ( void ) {
        /* Curve sanity test */
        elliptic_curve_ok ( &p256_curve );
 
-       /* Tests from http://point-at-infinity.org/ecc/nisttv */
+       /* Multiplication tests from http://point-at-infinity.org/ecc/nisttv */
        elliptic_multiply_ok ( &poi_1 );
        elliptic_multiply_ok ( &poi_2 );
        elliptic_multiply_ok ( &poi_2_20 );
@@ -170,6 +244,11 @@ static void p256_test_exec ( void ) {
        /* Invalid point tests */
        elliptic_multiply_ok ( &invalid_zero );
        elliptic_multiply_ok ( &invalid_one );
+
+       /* Addition tests from http://point-at-infinity.org/ecc/nisttv */
+       elliptic_add_ok ( &poi_2_2_4 );
+       elliptic_add_ok ( &poi_3_5_8 );
+       elliptic_add_ok ( &poi_1_n_1 );
 }
 
 /** P-256 self-test */
index 0b172c6489b9166b416cf80d563de42fa4cf62b0..c67cfbc796d04cbe3716403c3470e60aa1a25109 100644 (file)
@@ -197,6 +197,112 @@ ELLIPTIC_MULTIPLY_TEST ( invalid_one, &p384_curve,
                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 ),
        EXPECTED_FAIL );
 
+/* http://point-at-infinity.org/ecc/nisttv k=2 + k=2 => k=4 */
+ELLIPTIC_ADD_TEST ( poi_2_2_4, &p384_curve,
+       ADDEND ( 0x08, 0xd9, 0x99, 0x05, 0x7b, 0xa3, 0xd2, 0xd9,
+                0x69, 0x26, 0x00, 0x45, 0xc5, 0x5b, 0x97, 0xf0,
+                0x89, 0x02, 0x59, 0x59, 0xa6, 0xf4, 0x34, 0xd6,
+                0x51, 0xd2, 0x07, 0xd1, 0x9f, 0xb9, 0x6e, 0x9e,
+                0x4f, 0xe0, 0xe8, 0x6e, 0xbe, 0x0e, 0x64, 0xf8,
+                0x5b, 0x96, 0xa9, 0xc7, 0x52, 0x95, 0xdf, 0x61,
+                0x8e, 0x80, 0xf1, 0xfa, 0x5b, 0x1b, 0x3c, 0xed,
+                0xb7, 0xbf, 0xe8, 0xdf, 0xfd, 0x6d, 0xba, 0x74,
+                0xb2, 0x75, 0xd8, 0x75, 0xbc, 0x6c, 0xc4, 0x3e,
+                0x90, 0x4e, 0x50, 0x5f, 0x25, 0x6a, 0xb4, 0x25,
+                0x5f, 0xfd, 0x43, 0xe9, 0x4d, 0x39, 0xe2, 0x2d,
+                0x61, 0x50, 0x1e, 0x70, 0x0a, 0x94, 0x0e, 0x80 ),
+       AUGEND ( 0x08, 0xd9, 0x99, 0x05, 0x7b, 0xa3, 0xd2, 0xd9,
+                0x69, 0x26, 0x00, 0x45, 0xc5, 0x5b, 0x97, 0xf0,
+                0x89, 0x02, 0x59, 0x59, 0xa6, 0xf4, 0x34, 0xd6,
+                0x51, 0xd2, 0x07, 0xd1, 0x9f, 0xb9, 0x6e, 0x9e,
+                0x4f, 0xe0, 0xe8, 0x6e, 0xbe, 0x0e, 0x64, 0xf8,
+                0x5b, 0x96, 0xa9, 0xc7, 0x52, 0x95, 0xdf, 0x61,
+                0x8e, 0x80, 0xf1, 0xfa, 0x5b, 0x1b, 0x3c, 0xed,
+                0xb7, 0xbf, 0xe8, 0xdf, 0xfd, 0x6d, 0xba, 0x74,
+                0xb2, 0x75, 0xd8, 0x75, 0xbc, 0x6c, 0xc4, 0x3e,
+                0x90, 0x4e, 0x50, 0x5f, 0x25, 0x6a, 0xb4, 0x25,
+                0x5f, 0xfd, 0x43, 0xe9, 0x4d, 0x39, 0xe2, 0x2d,
+                0x61, 0x50, 0x1e, 0x70, 0x0a, 0x94, 0x0e, 0x80 ),
+       EXPECTED ( 0x13, 0x82, 0x51, 0xcd, 0x52, 0xac, 0x92, 0x98,
+                  0xc1, 0xc8, 0xaa, 0xd9, 0x77, 0x32, 0x1d, 0xeb,
+                  0x97, 0xe7, 0x09, 0xbd, 0x0b, 0x4c, 0xa0, 0xac,
+                  0xa5, 0x5d, 0xc8, 0xad, 0x51, 0xdc, 0xfc, 0x9d,
+                  0x15, 0x89, 0xa1, 0x59, 0x7e, 0x3a, 0x51, 0x20,
+                  0xe1, 0xef, 0xd6, 0x31, 0xc6, 0x3e, 0x18, 0x35,
+                  0xca, 0xca, 0xe2, 0x98, 0x69, 0xa6, 0x2e, 0x16,
+                  0x31, 0xe8, 0xa2, 0x81, 0x81, 0xab, 0x56, 0x61,
+                  0x6d, 0xc4, 0x5d, 0x91, 0x8a, 0xbc, 0x09, 0xf3,
+                  0xab, 0x0e, 0x63, 0xcf, 0x79, 0x2a, 0xa4, 0xdc,
+                  0xed, 0x73, 0x87, 0xbe, 0x37, 0xbb, 0xa5, 0x69,
+                  0x54, 0x9f, 0x1c, 0x02, 0xb2, 0x70, 0xed, 0x67 ) );
+
+/* http://point-at-infinity.org/ecc/nisttv k=3 + k=5 => k=8 */
+ELLIPTIC_ADD_TEST ( poi_3_5_8, &p384_curve,
+       ADDEND ( 0x07, 0x7a, 0x41, 0xd4, 0x60, 0x6f, 0xfa, 0x14,
+                0x64, 0x79, 0x3c, 0x7e, 0x5f, 0xdc, 0x7d, 0x98,
+                0xcb, 0x9d, 0x39, 0x10, 0x20, 0x2d, 0xcd, 0x06,
+                0xbe, 0xa4, 0xf2, 0x40, 0xd3, 0x56, 0x6d, 0xa6,
+                0xb4, 0x08, 0xbb, 0xae, 0x50, 0x26, 0x58, 0x0d,
+                0x02, 0xd7, 0xe5, 0xc7, 0x05, 0x00, 0xc8, 0x31,
+                0xc9, 0x95, 0xf7, 0xca, 0x0b, 0x0c, 0x42, 0x83,
+                0x7d, 0x0b, 0xbe, 0x96, 0x02, 0xa9, 0xfc, 0x99,
+                0x85, 0x20, 0xb4, 0x1c, 0x85, 0x11, 0x5a, 0xa5,
+                0xf7, 0x68, 0x4c, 0x0e, 0xdc, 0x11, 0x1e, 0xac,
+                0xc2, 0x4a, 0xbd, 0x6b, 0xe4, 0xb5, 0xd2, 0x98,
+                0xb6, 0x5f, 0x28, 0x60, 0x0a, 0x2f, 0x1d, 0xf1 ),
+       AUGEND ( 0x11, 0xde, 0x24, 0xa2, 0xc2, 0x51, 0xc7, 0x77,
+                0x57, 0x3c, 0xac, 0x5e, 0xa0, 0x25, 0xe4, 0x67,
+                0xf2, 0x08, 0xe5, 0x1d, 0xbf, 0xf9, 0x8f, 0xc5,
+                0x4f, 0x66, 0x61, 0xcb, 0xe5, 0x65, 0x83, 0xb0,
+                0x37, 0x88, 0x2f, 0x4a, 0x1c, 0xa2, 0x97, 0xe6,
+                0x0a, 0xbc, 0xdb, 0xc3, 0x83, 0x6d, 0x84, 0xbc,
+                0x8f, 0xa6, 0x96, 0xc7, 0x74, 0x40, 0xf9, 0x2d,
+                0x0f, 0x58, 0x37, 0xe9, 0x0a, 0x00, 0xe7, 0xc5,
+                0x28, 0x4b, 0x44, 0x77, 0x54, 0xd5, 0xde, 0xe8,
+                0x8c, 0x98, 0x65, 0x33, 0xb6, 0x90, 0x1a, 0xeb,
+                0x31, 0x77, 0x68, 0x6d, 0x0a, 0xe8, 0xfb, 0x33,
+                0x18, 0x44, 0x14, 0xab, 0xe6, 0xc1, 0x71, 0x3a ),
+       EXPECTED ( 0x16, 0x92, 0x77, 0x8e, 0xa5, 0x96, 0xe0, 0xbe,
+                  0x75, 0x11, 0x42, 0x97, 0xa6, 0xfa, 0x38, 0x34,
+                  0x45, 0xbf, 0x22, 0x7f, 0xbe, 0x58, 0x19, 0x0a,
+                  0x90, 0x0c, 0x3c, 0x73, 0x25, 0x6f, 0x11, 0xfb,
+                  0x5a, 0x32, 0x58, 0xd6, 0xf4, 0x03, 0xd5, 0xec,
+                  0xe6, 0xe9, 0xb2, 0x69, 0xd8, 0x22, 0xc8, 0x7d,
+                  0xdc, 0xd2, 0x36, 0x57, 0x00, 0xd4, 0x10, 0x6a,
+                  0x83, 0x53, 0x88, 0xba, 0x3d, 0xb8, 0xfd, 0x0e,
+                  0x22, 0x55, 0x4a, 0xdc, 0x6d, 0x52, 0x1c, 0xd4,
+                  0xbd, 0x1c, 0x30, 0xc2, 0xec, 0x0e, 0xec, 0x19,
+                  0x6b, 0xad, 0xe1, 0xe9, 0xcd, 0xd1, 0x70, 0x8d,
+                  0x6f, 0x6a, 0xbf, 0xa4, 0x02, 0x2b, 0x0a, 0xd2 ) );
+
+/* http://point-at-infinity.org/ecc/nisttv k=1 + k=n-1 => infinity */
+ELLIPTIC_ADD_TEST ( poi_1_n_1, &p384_curve,
+       ADDEND ( 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,
+                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 ),
+       AUGEND ( 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,
+                0xc9, 0xe8, 0x21, 0xb5, 0x69, 0xd9, 0xd3, 0x90,
+                0xa2, 0x61, 0x67, 0x40, 0x6d, 0x6d, 0x23, 0xd6,
+                0x07, 0x0b, 0xe2, 0x42, 0xd7, 0x65, 0xeb, 0x83,
+                0x16, 0x25, 0xce, 0xec, 0x4a, 0x0f, 0x47, 0x3e,
+                0xf5, 0x9f, 0x4e, 0x30, 0xe2, 0x81, 0x7e, 0x62,
+                0x85, 0xbc, 0xe2, 0x84, 0x6f, 0x15, 0xf1, 0xa0 ),
+       EXPECTED_FAIL );
+
 /**
  * Perform P-384 self-test
  *
@@ -206,7 +312,7 @@ static void p384_test_exec ( void ) {
        /* Curve sanity test */
        elliptic_curve_ok ( &p384_curve );
 
-       /* Tests from http://point-at-infinity.org/ecc/nisttv */
+       /* Multiplication tests from http://point-at-infinity.org/ecc/nisttv */
        elliptic_multiply_ok ( &poi_1 );
        elliptic_multiply_ok ( &poi_2 );
        elliptic_multiply_ok ( &poi_2_20 );
@@ -216,6 +322,11 @@ static void p384_test_exec ( void ) {
        /* Invalid point tests */
        elliptic_multiply_ok ( &invalid_zero );
        elliptic_multiply_ok ( &invalid_one );
+
+       /* Addition tests from http://point-at-infinity.org/ecc/nisttv */
+       elliptic_add_ok ( &poi_2_2_4 );
+       elliptic_add_ok ( &poi_3_5_8 );
+       elliptic_add_ok ( &poi_1_n_1 );
 }
 
 /** P-384 self-test */