Bug 1352039 - improvements for elliptic curve addition in mixed Jacobian-affine coordinates, r=ttaubert
authorFranziskus Kiefer <franziskuskiefer@gmail.com>
Tue, 04 Apr 2017 09:44:08 +0200
changeset 13391 6d1f5f958100
parent 13390 2fcd13ce29e5
child 13392 cd068f7ce6ae
child 13399 c4d2e29e8263
push id2216
push userfranziskuskiefer@gmail.com
push date2017-05-31 10:02 +0000
reviewersttaubert
bugs1352039
Bug 1352039 - improvements for elliptic curve addition in mixed Jacobian-affine coordinates, r=ttaubert Differential Revision: https://nss-review.dev.mozaws.net/D333
cpputil/scoped_ptrs.h
gtests/freebl_gtest/ecl_unittest.cc
gtests/freebl_gtest/freebl_gtest.gyp
gtests/freebl_gtest/mpi_unittest.cc
lib/freebl/ecl/ecp_jm.c
--- a/cpputil/scoped_ptrs.h
+++ b/cpputil/scoped_ptrs.h
@@ -29,16 +29,17 @@ struct ScopedDelete {
   void operator()(SECAlgorithmID* id) { SECOID_DestroyAlgorithmID(id, true); }
   void operator()(SECItem* item) { SECITEM_FreeItem(item, true); }
   void operator()(SECKEYPublicKey* key) { SECKEY_DestroyPublicKey(key); }
   void operator()(SECKEYPrivateKey* key) { SECKEY_DestroyPrivateKey(key); }
   void operator()(SECKEYPrivateKeyList* list) {
     SECKEY_DestroyPrivateKeyList(list);
   }
   void operator()(PK11URI* uri) { PK11URI_DestroyURI(uri); }
+  void operator()(PLArenaPool* arena) { PORT_FreeArena(arena, PR_FALSE); }
 };
 
 template <class T>
 struct ScopedMaybeDelete {
   void operator()(T* ptr) {
     if (ptr) {
       ScopedDelete del;
       del(ptr);
@@ -57,12 +58,13 @@ SCOPED(PK11SlotInfo);
 SCOPED(PK11SymKey);
 SCOPED(PRFileDesc);
 SCOPED(SECAlgorithmID);
 SCOPED(SECItem);
 SCOPED(SECKEYPublicKey);
 SCOPED(SECKEYPrivateKey);
 SCOPED(SECKEYPrivateKeyList);
 SCOPED(PK11URI);
+SCOPED(PLArenaPool);
 
 #undef SCOPED
 
 #endif  // scoped_ptrs_h__
new file mode 100644
--- /dev/null
+++ b/gtests/freebl_gtest/ecl_unittest.cc
@@ -0,0 +1,124 @@
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this file,
+// You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include "gtest/gtest.h"
+
+#include <stdint.h>
+
+#include "blapi.h"
+#include "scoped_ptrs.h"
+#include "secerr.h"
+
+namespace nss_test {
+
+class ECLTest : public ::testing::Test {
+ protected:
+  const ECCurveName GetCurveName(std::string name) {
+    if (name == "P256") return ECCurve_NIST_P256;
+    if (name == "P384") return ECCurve_NIST_P384;
+    if (name == "P521") return ECCurve_NIST_P521;
+    return ECCurve_pastLastCurve;
+  }
+  std::vector<uint8_t> hexStringToBytes(std::string s) {
+    std::vector<uint8_t> bytes;
+    for (size_t i = 0; i < s.length(); i += 2) {
+      bytes.push_back(std::stoul(s.substr(i, 2), nullptr, 16));
+    }
+    return bytes;
+  }
+  std::string bytesToHexString(std::vector<uint8_t> bytes) {
+    std::stringstream s;
+    for (auto b : bytes) {
+      s << std::setfill('0') << std::setw(2) << std::uppercase << std::hex
+        << static_cast<int>(b);
+    }
+    return s.str();
+  }
+  void ecName2params(const std::string curve, SECItem *params) {
+    SECOidData *oidData = nullptr;
+
+    switch (GetCurveName(curve)) {
+      case ECCurve_NIST_P256:
+        oidData = SECOID_FindOIDByTag(SEC_OID_ANSIX962_EC_PRIME256V1);
+        break;
+      case ECCurve_NIST_P384:
+        oidData = SECOID_FindOIDByTag(SEC_OID_SECG_EC_SECP384R1);
+        break;
+      case ECCurve_NIST_P521:
+        oidData = SECOID_FindOIDByTag(SEC_OID_SECG_EC_SECP521R1);
+        break;
+      default:
+        FAIL();
+    }
+    ASSERT_NE(oidData, nullptr);
+
+    if (SECITEM_AllocItem(nullptr, params, (2 + oidData->oid.len)) == nullptr) {
+      FAIL() << "Couldn't allocate memory for OID.";
+    }
+    params->data[0] = SEC_ASN1_OBJECT_ID;
+    params->data[1] = oidData->oid.len;
+    memcpy(params->data + 2, oidData->oid.data, oidData->oid.len);
+  }
+
+  void TestECDH_Derive(const std::string p, const std::string secret,
+                       const std::string group_name, const std::string result,
+                       const SECStatus expected_status) {
+    ECParams ecParams = {0};
+    ScopedSECItem ecEncodedParams(SECITEM_AllocItem(nullptr, nullptr, 0U));
+    ScopedPLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
+
+    ASSERT_TRUE(arena && ecEncodedParams);
+
+    ecName2params(group_name, ecEncodedParams.get());
+    EC_FillParams(arena.get(), ecEncodedParams.get(), &ecParams);
+
+    std::vector<uint8_t> p_bytes = hexStringToBytes(p);
+    ASSERT_GT(p_bytes.size(), 0U);
+    SECItem public_value = {siBuffer, p_bytes.data(),
+                            static_cast<unsigned int>(p_bytes.size())};
+
+    std::vector<uint8_t> secret_bytes = hexStringToBytes(secret);
+    ASSERT_GT(secret_bytes.size(), 0U);
+    SECItem secret_value = {siBuffer, secret_bytes.data(),
+                            static_cast<unsigned int>(secret_bytes.size())};
+
+    ScopedSECItem derived_secret(SECITEM_AllocItem(nullptr, nullptr, 0U));
+
+    SECStatus rv = ECDH_Derive(&public_value, &ecParams, &secret_value, false,
+                               derived_secret.get());
+    ASSERT_EQ(expected_status, rv);
+    if (expected_status != SECSuccess) {
+      // Abort when we expect an error.
+      return;
+    }
+
+    std::string derived_result = bytesToHexString(std::vector<uint8_t>(
+        derived_secret->data, derived_secret->data + derived_secret->len));
+    std::cout << "derived secret: " << derived_result << std::endl;
+    EXPECT_EQ(derived_result, result);
+  }
+};
+
+TEST_F(ECLTest, TestECDH_DeriveP256) {
+  TestECDH_Derive(
+      "045ce5c643dffa402bc1837bbcbc223e51d06f20200470d341adfa9deed1bba10e850a16"
+      "368b673732a5c220a778990b22a0e74cdc3b22c7410b9dd552a5635497",
+      "971", "P256", "0", SECFailure);
+}
+TEST_F(ECLTest, TestECDH_DeriveP521) {
+  TestECDH_Derive(
+      "04"
+      "00c6858e06b70404e9cd9e3ecb662395b4429c648139053fb521f828af606b4d3dbaa14b"
+      "5e77efe75928fe1dc127a2ffa8de3348b3c1856a429bf97e7e31c2e5bd66"
+      "011839296a789a3bc0045c8a5fb42c7d1bd998f54449579b446817afbd17273e662c97ee"
+      "72995ef42640c550b9013fad0761353c7086a272c24088be94769fd16650",
+      "01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa5186"
+      "8783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e913863f7",
+      "P521",
+      "01BC33425E72A12779EACB2EDCC5B63D1281F7E86DBC7BF99A7ABD0CFE367DE4666D6EDB"
+      "B8525BFFE5222F0702C3096DEC0884CE572F5A15C423FDF44D01DD99C61D",
+      SECSuccess);
+}
+
+}  // nss_test
--- a/gtests/freebl_gtest/freebl_gtest.gyp
+++ b/gtests/freebl_gtest/freebl_gtest.gyp
@@ -8,16 +8,17 @@
   ],
   'targets': [
     {
       'target_name': 'freebl_gtest',
       'type': 'executable',
       'sources': [
         'mpi_unittest.cc',
         'dh_unittest.cc',
+        'ecl_unittest.cc',
         '<(DEPTH)/gtests/common/gtests.cc'
       ],
       'dependencies': [
         '<(DEPTH)/exports.gyp:nss_exports',
         '<(DEPTH)/lib/util/util.gyp:nssutil3',
         '<(DEPTH)/gtests/google_test/google_test.gyp:gtest',
         '<(DEPTH)/lib/nss/nss.gyp:nss_static',
         '<(DEPTH)/lib/pk11wrap/pk11wrap.gyp:pk11wrap_static',
@@ -60,16 +61,17 @@
       'defines': [
         'NSS_USE_STATIC_LIBS'
       ],
     },
   ],
   'target_defaults': {
     'include_dirs': [
       '<(DEPTH)/lib/freebl/mpi',
+      '<(DEPTH)/lib/freebl/',
     ],
     # For test builds we have to set MPI defines.
     'conditions': [
       [ 'ct_verif==1', {
         'defines': [
           'CT_VERIF',
         ],
       }],
--- a/gtests/freebl_gtest/mpi_unittest.cc
+++ b/gtests/freebl_gtest/mpi_unittest.cc
@@ -1,21 +1,16 @@
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this file,
 // You can obtain one at http://mozilla.org/MPL/2.0/.
 
-#include "secdert.h"
-#include "secitem.h"
-#include "secport.h"
-
 #include "gtest/gtest.h"
 
 #include <stdint.h>
 #include <string.h>
-#include <string>
 
 #ifdef __MACH__
 #include <mach/clock.h>
 #include <mach/mach.h>
 #endif
 
 #include "mpi.h"
 namespace nss_test {
--- a/lib/freebl/ecl/ecp_jm.c
+++ b/lib/freebl/ecl/ecp_jm.c
@@ -122,16 +122,27 @@ ec_GFp_pt_add_jm_aff(const mp_int *px, c
     }
 
     /* A = qx * pz^2, B = qy * pz^3 */
     MP_CHECKOK(group->meth->field_sqr(pz, A, group->meth));
     MP_CHECKOK(group->meth->field_mul(A, pz, B, group->meth));
     MP_CHECKOK(group->meth->field_mul(A, qx, A, group->meth));
     MP_CHECKOK(group->meth->field_mul(B, qy, B, group->meth));
 
+    /* Check P == Q */
+    if (mp_cmp(A, px) == 0) {
+        if (mp_cmp(B, py) == 0) {
+            /* If Px == Qx && Py == Qy, double P. */
+            return ec_GFp_pt_dbl_jm(px, py, pz, paz4, rx, ry, rz, raz4,
+                                    scratch, group);
+        }
+        /* If Px == Qx && Py != Qy, return point at infinity. */
+        return ec_GFp_pt_set_inf_jac(rx, ry, rz);
+    }
+
     /* C = A - px, D = B - py */
     MP_CHECKOK(group->meth->field_sub(A, px, C, group->meth));
     MP_CHECKOK(group->meth->field_sub(B, py, D, group->meth));
 
     /* C2 = C^2, C3 = C^3 */
     MP_CHECKOK(group->meth->field_sqr(C, C2, group->meth));
     MP_CHECKOK(group->meth->field_mul(C, C2, C3, group->meth));