Przeglądaj źródła

allow set prv.key from remote, validate new prv.key

taco 6 miesięcy temu
rodzic
commit
96ef5e5efe

+ 13 - 9
examples/companion_radio/MyMesh.cpp

@@ -1294,16 +1294,20 @@ void MyMesh::handleCmdFrame(size_t len) {
 #endif
   } else if (cmd_frame[0] == CMD_IMPORT_PRIVATE_KEY && len >= 65) {
 #if ENABLE_PRIVATE_KEY_IMPORT
-    mesh::LocalIdentity identity;
-    identity.readFrom(&cmd_frame[1], 64);
-    if (_store->saveMainIdentity(identity)) {
-      self_id = identity;
-      writeOKFrame();
-      // re-load contacts, to invalidate ecdh shared_secrets
-      resetContacts();
-      _store->loadContacts(this);
+    if (!mesh::LocalIdentity::validatePrivateKey(&cmd_frame[1])) {
+        writeErrFrame(ERR_CODE_ILLEGAL_ARG); // invalid key
     } else {
-      writeErrFrame(ERR_CODE_FILE_IO_ERROR);
+        mesh::LocalIdentity identity;
+        identity.readFrom(&cmd_frame[1], 64);
+        if (_store->saveMainIdentity(identity)) {
+          self_id = identity;
+          writeOKFrame();
+          // re-load contacts, to invalidate ecdh shared_secrets
+          resetContacts();
+          _store->loadContacts(this);
+        } else {
+          writeErrFrame(ERR_CODE_FILE_IO_ERROR);
+        }
     }
 #else
     writeDisabledFrame();

+ 1 - 2
examples/simple_repeater/MyMesh.cpp

@@ -968,7 +968,6 @@ void MyMesh::formatPacketStatsReply(char *reply) {
 }
 
 void MyMesh::saveIdentity(const mesh::LocalIdentity &new_id) {
-  self_id = new_id;
 #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM)
   IdentityStore store(*_fs, "");
 #elif defined(ESP32)
@@ -978,7 +977,7 @@ void MyMesh::saveIdentity(const mesh::LocalIdentity &new_id) {
 #else
 #error "need to define saveIdentity()"
 #endif
-  store.save("_main", self_id);
+  store.save("_main", new_id);
 }
 
 void MyMesh::clearStats() {

+ 1 - 2
examples/simple_room_server/MyMesh.cpp

@@ -720,7 +720,6 @@ void MyMesh::setTxPower(uint8_t power_dbm) {
 }
 
 void MyMesh::saveIdentity(const mesh::LocalIdentity &new_id) {
-  self_id = new_id;
 #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM)
   IdentityStore store(*_fs, "");
 #elif defined(ESP32)
@@ -730,7 +729,7 @@ void MyMesh::saveIdentity(const mesh::LocalIdentity &new_id) {
 #else
 #error "need to define saveIdentity()"
 #endif
-  store.save("_main", self_id);
+  store.save("_main", new_id);
 }
 
 void MyMesh::clearStats() {

+ 1 - 2
examples/simple_sensor/SensorMesh.cpp

@@ -765,7 +765,6 @@ bool SensorMesh::formatFileSystem() {
 }
 
 void SensorMesh::saveIdentity(const mesh::LocalIdentity& new_id) {
-  self_id = new_id;
 #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM)
   IdentityStore store(*_fs, "");
 #elif defined(ESP32)
@@ -775,7 +774,7 @@ void SensorMesh::saveIdentity(const mesh::LocalIdentity& new_id) {
 #else
   #error "need to define saveIdentity()"
 #endif
-  store.save("_main", self_id);
+  store.save("_main", new_id);
 }
 
 void SensorMesh::applyTempRadioParams(float freq, float bw, uint8_t sf, uint8_t cr, int timeout_mins) {

+ 44 - 0
src/Identity.cpp

@@ -48,6 +48,50 @@ LocalIdentity::LocalIdentity(RNG* rng) {
   ed25519_create_keypair(pub_key, prv_key, seed);
 }
 
+bool LocalIdentity::validatePrivateKey(const uint8_t prv[64]) {
+    uint8_t pub[32];
+    ed25519_derive_pub(pub, prv); // derive public key from given private key
+
+    // disallow 00 or FF prefixed public keys
+    if (pub[0] == 0x00 || pub[0] == 0xFF) return false;
+
+    // known good test client keypair
+    const uint8_t test_client_prv[64] = {
+      0x70, 0x65, 0xe1, 0x8f, 0xd9, 0xfa, 0xbb, 0x70,
+      0xc1, 0xed, 0x90, 0xdc, 0xa1, 0x99, 0x07, 0xde,
+      0x69, 0x8c, 0x88, 0xb7, 0x09, 0xea, 0x14, 0x6e,
+      0xaf, 0xd9, 0x3d, 0x9b, 0x83, 0x0c, 0x7b, 0x60,
+      0xc4, 0x68, 0x11, 0x93, 0xc7, 0x9b, 0xbc, 0x39,
+      0x94, 0x5b, 0xa8, 0x06, 0x41, 0x04, 0xbb, 0x61,
+      0x8f, 0x8f, 0xd7, 0xa8, 0x4a, 0x0a, 0xf6, 0xf5,
+      0x70, 0x33, 0xd6, 0xe8, 0xdd, 0xcd, 0x64, 0x71
+    };
+    const uint8_t test_client_pub[32] = {
+      0x1e, 0xc7, 0x71, 0x75, 0xb0, 0x91, 0x8e, 0xd2,
+      0x06, 0xf9, 0xae, 0x04, 0xec, 0x13, 0x6d, 0x6d,
+      0x5d, 0x43, 0x15, 0xbb, 0x26, 0x30, 0x54, 0x27,
+      0xf6, 0x45, 0xb4, 0x92, 0xe9, 0x35, 0x0c, 0x10
+    };
+
+    uint8_t ss1[32], ss2[32];
+
+    // shared secret we calculte from test client pubkey and given private key
+    ed25519_key_exchange(ss1, test_client_pub, prv);
+
+    // shared secret they calculate from our derived public key and test client private key
+    ed25519_key_exchange(ss2, pub, test_client_prv);
+
+    // check that both shared secrets match
+    if (memcmp(ss1, ss2, 32) != 0) return false;
+
+    // reject all-zero shared secret
+    for (int i = 0; i < 32; i++) {
+        if (ss1[i] != 0) return true;
+    }
+
+    return false;
+}
+
 bool LocalIdentity::readFrom(Stream& s) {
   bool success = (s.readBytes(pub_key, PUB_KEY_SIZE) == PUB_KEY_SIZE);
   success = success && (s.readBytes(prv_key, PRV_KEY_SIZE) == PRV_KEY_SIZE);

+ 7 - 0
src/Identity.h

@@ -76,6 +76,13 @@ public:
   */
   void calcSharedSecret(uint8_t* secret, const uint8_t* other_pub_key) const;
 
+  /**
+   * \brief  Validates that a given private key can be used for ECDH / shared-secret operations.
+   * \param  prv IN - the private key to validate (must be PRV_KEY_SIZE bytes)
+   * \returns true, if the private key is valid for login.
+  */
+  static bool validatePrivateKey(const uint8_t prv[64]);
+
   bool readFrom(Stream& s);
   bool writeTo(Stream& s) const;
   void printTo(Stream& s) const;

+ 6 - 5
src/helpers/CommonCLI.cpp

@@ -416,17 +416,18 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch
         StrHelper::strncpy(_prefs->guest_password, &config[15], sizeof(_prefs->guest_password));
         savePrefs();
         strcpy(reply, "OK");
-      } else if (sender_timestamp == 0 &&
-                 memcmp(config, "prv.key ", 8) == 0) { // from serial command line only
+      } else if (memcmp(config, "prv.key ", 8) == 0) {
         uint8_t prv_key[PRV_KEY_SIZE];
         bool success = mesh::Utils::fromHex(prv_key, PRV_KEY_SIZE, &config[8]);
-        if (success) {
+        // only allow rekey if key is valid
+        if (success && mesh::LocalIdentity::validatePrivateKey(prv_key)) {
           mesh::LocalIdentity new_id;
           new_id.readFrom(prv_key, PRV_KEY_SIZE);
           _callbacks->saveIdentity(new_id);
-          strcpy(reply, "OK");
+          strcpy(reply, "OK, reboot to apply! New pubkey: ");
+          mesh::Utils::toHex(&reply[33], new_id.pub_key, PUB_KEY_SIZE);
         } else {
-          strcpy(reply, "Error, invalid key");
+          strcpy(reply, "Error, bad key");
         }
       } else if (memcmp(config, "name ", 5) == 0) {
         if (isValidName(&config[5])) {