Browse Source

Support working with ed25519 seeds in addition to raw keypairs.

Our ed25519 library uses a representation of its key pair that
is largely incompatible with modern implementations, which mostly
work with the original 32-byte seed; Peters' impentation represents
the private key as the clamped sha512 of the seed.

This change:

- preserves the original seed when generating keys
- adds CLI commands to obtain the seed via `get prv.seed`,
  under the same conditions as `get prv.key` is allowed
- adds support for `set prv.key` to supply a seed, in which
  case the keypair will be re-generated from it.  This is
  mostly to enable external key management using modern
  libraries, but could also be of use on devices where
  we don't have a trustworthy entropy source.

I split Identity::writeTo(uint8_t*,size_t) into explicit forms for
the thing being written; the original implementation wrote a
different thing depending on the length, which would be ambiguous
between pubkey and seed and cumbersome if it tried to return all
three in one long buffer. Identity::readFrom() did not have that
ambiguity problem because keys can't be set from pubkey alone,
though it might be preferable to split readFrom() up as well
and not use magic length values.
pull/1055/head
Devin Carraway 8 months ago
parent
commit
a2df37356f
  1. 36
      src/Identity.cpp
  2. 15
      src/Identity.h
  3. 18
      src/helpers/CommonCLI.cpp

36
src/Identity.cpp

@ -43,7 +43,6 @@ LocalIdentity::LocalIdentity(const char* prv_hex, const char* pub_hex) : Identit
} }
LocalIdentity::LocalIdentity(RNG* rng) { LocalIdentity::LocalIdentity(RNG* rng) {
uint8_t seed[SEED_SIZE];
rng->random(seed, SEED_SIZE); rng->random(seed, SEED_SIZE);
ed25519_create_keypair(pub_key, prv_key, seed); ed25519_create_keypair(pub_key, prv_key, seed);
} }
@ -51,12 +50,17 @@ LocalIdentity::LocalIdentity(RNG* rng) {
bool LocalIdentity::readFrom(Stream& s) { bool LocalIdentity::readFrom(Stream& s) {
bool success = (s.readBytes(pub_key, PUB_KEY_SIZE) == PUB_KEY_SIZE); bool success = (s.readBytes(pub_key, PUB_KEY_SIZE) == PUB_KEY_SIZE);
success = success && (s.readBytes(prv_key, PRV_KEY_SIZE) == PRV_KEY_SIZE); success = success && (s.readBytes(prv_key, PRV_KEY_SIZE) == PRV_KEY_SIZE);
memset(seed, 0, SEED_SIZE);
if (success) {
s.readBytes(seed, SEED_SIZE);
}
return success; return success;
} }
bool LocalIdentity::writeTo(Stream& s) const { bool LocalIdentity::writeTo(Stream& s) const {
bool success = (s.write(pub_key, PUB_KEY_SIZE) == PUB_KEY_SIZE); bool success = (s.write(pub_key, PUB_KEY_SIZE) == PUB_KEY_SIZE);
success = success && (s.write(prv_key, PRV_KEY_SIZE) == PRV_KEY_SIZE); success = success && (s.write(prv_key, PRV_KEY_SIZE) == PRV_KEY_SIZE);
success = success && (s.write(seed, SEED_SIZE) == SEED_SIZE);
return success; return success;
} }
@ -65,26 +69,38 @@ void LocalIdentity::printTo(Stream& s) const {
s.print("prv_key: "); Utils::printHex(s, prv_key, PRV_KEY_SIZE); s.println(); s.print("prv_key: "); Utils::printHex(s, prv_key, PRV_KEY_SIZE); s.println();
} }
size_t LocalIdentity::writeTo(uint8_t* dest, size_t max_len) { size_t LocalIdentity::writePubkeyTo(uint8_t* dest, size_t max_len) {
if (max_len < PUB_KEY_SIZE) return 0; // not big enough
memcpy(dest, pub_key, PUB_KEY_SIZE);
return PUB_KEY_SIZE;
}
size_t LocalIdentity::writePrvkeyTo(uint8_t* dest, size_t max_len) {
if (max_len < PRV_KEY_SIZE) return 0; // not big enough if (max_len < PRV_KEY_SIZE) return 0; // not big enough
memcpy(dest, prv_key, PRV_KEY_SIZE);
return PRV_KEY_SIZE;
}
if (max_len < PRV_KEY_SIZE + PUB_KEY_SIZE) { // only room for prv_key size_t LocalIdentity::writeSeedTo(uint8_t* dest, size_t max_len) {
memcpy(dest, prv_key, PRV_KEY_SIZE); if (max_len < SEED_SIZE) return 0; // not big enough
return PRV_KEY_SIZE; memcpy(dest, seed, SEED_SIZE);
} return SEED_SIZE;
memcpy(dest, prv_key, PRV_KEY_SIZE); // otherwise can fit prv + pub keys
memcpy(&dest[PRV_KEY_SIZE], pub_key, PUB_KEY_SIZE);
return PRV_KEY_SIZE + PUB_KEY_SIZE;
} }
void LocalIdentity::readFrom(const uint8_t* src, size_t len) { void LocalIdentity::readFrom(const uint8_t* src, size_t len) {
if (len == PRV_KEY_SIZE + PUB_KEY_SIZE) { // has prv + pub keys if (len == PRV_KEY_SIZE + PUB_KEY_SIZE) { // has prv + pub keys
memcpy(prv_key, src, PRV_KEY_SIZE); memcpy(prv_key, src, PRV_KEY_SIZE);
memcpy(pub_key, &src[PRV_KEY_SIZE], PUB_KEY_SIZE); memcpy(pub_key, &src[PRV_KEY_SIZE], PUB_KEY_SIZE);
memset(seed, 0, SEED_SIZE);
} else if (len == PRV_KEY_SIZE) { } else if (len == PRV_KEY_SIZE) {
memcpy(prv_key, src, PRV_KEY_SIZE); memcpy(prv_key, src, PRV_KEY_SIZE);
// now need to re-calculate the pub_key // now need to re-calculate the pub_key
ed25519_derive_pub(pub_key, prv_key); ed25519_derive_pub(pub_key, prv_key);
memset(seed, 0, SEED_SIZE);
} else if (len == SEED_SIZE) {
memcpy(seed, src, SEED_SIZE);
// re-generate the keypair from the given seed
ed25519_create_keypair(pub_key, prv_key, seed);
} }
} }
@ -96,4 +112,4 @@ void LocalIdentity::calcSharedSecret(uint8_t* secret, const uint8_t* other_pub_k
ed25519_key_exchange(secret, other_pub_key, prv_key); ed25519_key_exchange(secret, other_pub_key, prv_key);
} }
} }

15
src/Identity.h

@ -46,6 +46,7 @@ public:
*/ */
class LocalIdentity : public Identity { class LocalIdentity : public Identity {
uint8_t prv_key[PRV_KEY_SIZE]; uint8_t prv_key[PRV_KEY_SIZE];
uint8_t seed[SEED_SIZE];
public: public:
LocalIdentity(); LocalIdentity();
LocalIdentity(const char* prv_hex, const char* pub_hex); LocalIdentity(const char* prv_hex, const char* pub_hex);
@ -76,7 +77,19 @@ public:
bool readFrom(Stream& s); bool readFrom(Stream& s);
bool writeTo(Stream& s) const; bool writeTo(Stream& s) const;
void printTo(Stream& s) const; void printTo(Stream& s) const;
size_t writeTo(uint8_t* dest, size_t max_len); size_t writePubkeyTo(uint8_t* dest, size_t max_len);
size_t writePrvkeyTo(uint8_t* dest, size_t max_len);
size_t writeSeedTo(uint8_t* dest, size_t max_len);
/**
* \brief Set the Ed25519 keypair.
* \param src IN - the source for the key(s) or seed
* \param len IN - length of the input; if equal to SEED_SIZE, src is
* assumed to be a new seed, from which new private and public keys
* will be generated. If equal to PRV_KEY_SIZE, the corresponding
* public key will be re-generated. If equal to PRV_KEY_SIZE+
* PUB_KEY_SIZE, no key regen is needed. The seed can only later
* be obtained via the `get prv.seed` CLI if SEED_SIZE is used.
*/
void readFrom(const uint8_t* src, size_t len); void readFrom(const uint8_t* src, size_t len);
}; };

18
src/helpers/CommonCLI.cpp

@ -268,9 +268,14 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch
sprintf(reply, "> %s", _prefs->guest_password); sprintf(reply, "> %s", _prefs->guest_password);
} else if (sender_timestamp == 0 && memcmp(config, "prv.key", 7) == 0) { // from serial command line only } else if (sender_timestamp == 0 && memcmp(config, "prv.key", 7) == 0) { // from serial command line only
uint8_t prv_key[PRV_KEY_SIZE]; uint8_t prv_key[PRV_KEY_SIZE];
int len = _callbacks->getSelfId().writeTo(prv_key, PRV_KEY_SIZE); int len = _callbacks->getSelfId().writePrvkeyTo(prv_key, PRV_KEY_SIZE);
mesh::Utils::toHex(tmp, prv_key, len); mesh::Utils::toHex(tmp, prv_key, len);
sprintf(reply, "> %s", tmp); sprintf(reply, "> %s", tmp);
} else if (sender_timestamp == 0 && memcmp(config, "prv.seed", 8) == 0) { // from serial command line only
uint8_t seed[SEED_SIZE];
int len = _callbacks->getSelfId().writeSeedTo(seed, SEED_SIZE);
mesh::Utils::toHex(tmp, seed, len);
sprintf(reply, "> %s", tmp);
} else if (memcmp(config, "name", 4) == 0) { } else if (memcmp(config, "name", 4) == 0) {
sprintf(reply, "> %s", _prefs->node_name); sprintf(reply, "> %s", _prefs->node_name);
} else if (memcmp(config, "repeat", 6) == 0) { } else if (memcmp(config, "repeat", 6) == 0) {
@ -393,6 +398,17 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch
} else { } else {
strcpy(reply, "Error, invalid key"); strcpy(reply, "Error, invalid key");
} }
} else if (sender_timestamp == 0 && memcmp(config, "prv.seed ", 9) == 0) { // from serial command line only
uint8_t seed[SEED_SIZE];
bool success = mesh::Utils::fromHex(seed, SEED_SIZE, &config[9]);
if (success) {
mesh::LocalIdentity new_id;
new_id.readFrom(seed, SEED_SIZE);
_callbacks->saveIdentity(new_id);
strcpy(reply, "OK");
} else {
strcpy(reply, "Error, invalid seed");
}
} else if (memcmp(config, "name ", 5) == 0) { } else if (memcmp(config, "name ", 5) == 0) {
StrHelper::strncpy(_prefs->node_name, &config[5], sizeof(_prefs->node_name)); StrHelper::strncpy(_prefs->node_name, &config[5], sizeof(_prefs->node_name));
savePrefs(); savePrefs();

Loading…
Cancel
Save