Skip to content

Commit 1a7d165

Browse files
author
Devin Carraway
committed
Support working with ed25519 seeds in addition to raw keypairs.
Our ed25519 library uses a representation of its key pair that is incompatible with most modern implementations, which usually 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.
1 parent 7755400 commit 1a7d165

File tree

3 files changed

+57
-12
lines changed

3 files changed

+57
-12
lines changed

src/Identity.cpp

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -43,20 +43,24 @@ LocalIdentity::LocalIdentity(const char* prv_hex, const char* pub_hex) : Identit
4343
}
4444

4545
LocalIdentity::LocalIdentity(RNG* rng) {
46-
uint8_t seed[SEED_SIZE];
4746
rng->random(seed, SEED_SIZE);
4847
ed25519_create_keypair(pub_key, prv_key, seed);
4948
}
5049

5150
bool LocalIdentity::readFrom(Stream& s) {
5251
bool success = (s.readBytes(pub_key, PUB_KEY_SIZE) == PUB_KEY_SIZE);
5352
success = success && (s.readBytes(prv_key, PRV_KEY_SIZE) == PRV_KEY_SIZE);
53+
memset(seed, 0, SEED_SIZE);
54+
if (success) {
55+
s.readBytes(seed, SEED_SIZE);
56+
}
5457
return success;
5558
}
5659

5760
bool LocalIdentity::writeTo(Stream& s) const {
5861
bool success = (s.write(pub_key, PUB_KEY_SIZE) == PUB_KEY_SIZE);
5962
success = success && (s.write(prv_key, PRV_KEY_SIZE) == PRV_KEY_SIZE);
63+
success = success && (s.write(seed, SEED_SIZE) == SEED_SIZE);
6064
return success;
6165
}
6266

@@ -65,26 +69,38 @@ void LocalIdentity::printTo(Stream& s) const {
6569
s.print("prv_key: "); Utils::printHex(s, prv_key, PRV_KEY_SIZE); s.println();
6670
}
6771

68-
size_t LocalIdentity::writeTo(uint8_t* dest, size_t max_len) {
72+
size_t LocalIdentity::writePubkeyTo(uint8_t* dest, size_t max_len) {
73+
if (max_len < PUB_KEY_SIZE) return 0; // not big enough
74+
memcpy(dest, pub_key, PUB_KEY_SIZE);
75+
return PUB_KEY_SIZE;
76+
}
77+
78+
size_t LocalIdentity::writePrvkeyTo(uint8_t* dest, size_t max_len) {
6979
if (max_len < PRV_KEY_SIZE) return 0; // not big enough
80+
memcpy(dest, prv_key, PRV_KEY_SIZE);
81+
return PRV_KEY_SIZE;
82+
}
7083

71-
if (max_len < PRV_KEY_SIZE + PUB_KEY_SIZE) { // only room for prv_key
72-
memcpy(dest, prv_key, PRV_KEY_SIZE);
73-
return PRV_KEY_SIZE;
74-
}
75-
memcpy(dest, prv_key, PRV_KEY_SIZE); // otherwise can fit prv + pub keys
76-
memcpy(&dest[PRV_KEY_SIZE], pub_key, PUB_KEY_SIZE);
77-
return PRV_KEY_SIZE + PUB_KEY_SIZE;
84+
size_t LocalIdentity::writeSeedTo(uint8_t* dest, size_t max_len) {
85+
if (max_len < SEED_SIZE) return 0; // not big enough
86+
memcpy(dest, seed, SEED_SIZE);
87+
return SEED_SIZE;
7888
}
7989

8090
void LocalIdentity::readFrom(const uint8_t* src, size_t len) {
8191
if (len == PRV_KEY_SIZE + PUB_KEY_SIZE) { // has prv + pub keys
8292
memcpy(prv_key, src, PRV_KEY_SIZE);
8393
memcpy(pub_key, &src[PRV_KEY_SIZE], PUB_KEY_SIZE);
94+
memset(seed, 0, SEED_SIZE);
8495
} else if (len == PRV_KEY_SIZE) {
8596
memcpy(prv_key, src, PRV_KEY_SIZE);
8697
// now need to re-calculate the pub_key
8798
ed25519_derive_pub(pub_key, prv_key);
99+
memset(seed, 0, SEED_SIZE);
100+
} else if (len == SEED_SIZE) {
101+
memcpy(seed, src, SEED_SIZE);
102+
// re-generate the keypair from the given seed
103+
ed25519_create_keypair(pub_key, prv_key, seed);
88104
}
89105
}
90106

@@ -96,4 +112,4 @@ void LocalIdentity::calcSharedSecret(uint8_t* secret, const uint8_t* other_pub_k
96112
ed25519_key_exchange(secret, other_pub_key, prv_key);
97113
}
98114

99-
}
115+
}

src/Identity.h

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ class Identity {
4646
*/
4747
class LocalIdentity : public Identity {
4848
uint8_t prv_key[PRV_KEY_SIZE];
49+
uint8_t seed[SEED_SIZE];
4950
public:
5051
LocalIdentity();
5152
LocalIdentity(const char* prv_hex, const char* pub_hex);
@@ -76,7 +77,19 @@ class LocalIdentity : public Identity {
7677
bool readFrom(Stream& s);
7778
bool writeTo(Stream& s) const;
7879
void printTo(Stream& s) const;
79-
size_t writeTo(uint8_t* dest, size_t max_len);
80+
size_t writePubkeyTo(uint8_t* dest, size_t max_len);
81+
size_t writePrvkeyTo(uint8_t* dest, size_t max_len);
82+
size_t writeSeedTo(uint8_t* dest, size_t max_len);
83+
/**
84+
* \brief Set the Ed25519 keypair.
85+
* \param src IN - the source for the key(s) or seed
86+
* \param len IN - length of the input; if equal to SEED_SIZE, src is
87+
* assumed to be a new seed, from which new private and public keys
88+
* will be generated. If equal to PRV_KEY_SIZE, the corresponding
89+
* public key will be re-generated. If equal to PRV_KEY_SIZE+
90+
* PUB_KEY_SIZE, no key regen is needed. The seed can only later
91+
* be obtained via the `get prv.seed` CLI if SEED_SIZE is used.
92+
*/
8093
void readFrom(const uint8_t* src, size_t len);
8194
};
8295

src/helpers/CommonCLI.cpp

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -268,9 +268,14 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch
268268
sprintf(reply, "> %s", _prefs->guest_password);
269269
} else if (sender_timestamp == 0 && memcmp(config, "prv.key", 7) == 0) { // from serial command line only
270270
uint8_t prv_key[PRV_KEY_SIZE];
271-
int len = _callbacks->getSelfId().writeTo(prv_key, PRV_KEY_SIZE);
271+
int len = _callbacks->getSelfId().writePrvkeyTo(prv_key, PRV_KEY_SIZE);
272272
mesh::Utils::toHex(tmp, prv_key, len);
273273
sprintf(reply, "> %s", tmp);
274+
} else if (sender_timestamp == 0 && memcmp(config, "prv.seed", 8) == 0) { // from serial command line only
275+
uint8_t seed[SEED_SIZE];
276+
int len = _callbacks->getSelfId().writeSeedTo(seed, SEED_SIZE);
277+
mesh::Utils::toHex(tmp, seed, len);
278+
sprintf(reply, "> %s", tmp);
274279
} else if (memcmp(config, "name", 4) == 0) {
275280
sprintf(reply, "> %s", _prefs->node_name);
276281
} else if (memcmp(config, "repeat", 6) == 0) {
@@ -393,6 +398,17 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch
393398
} else {
394399
strcpy(reply, "Error, invalid key");
395400
}
401+
} else if (sender_timestamp == 0 && memcmp(config, "prv.seed ", 9) == 0) { // from serial command line only
402+
uint8_t seed[SEED_SIZE];
403+
bool success = mesh::Utils::fromHex(seed, SEED_SIZE, &config[9]);
404+
if (success) {
405+
mesh::LocalIdentity new_id;
406+
new_id.readFrom(seed, SEED_SIZE);
407+
_callbacks->saveIdentity(new_id);
408+
strcpy(reply, "OK");
409+
} else {
410+
strcpy(reply, "Error, invalid seed");
411+
}
396412
} else if (memcmp(config, "name ", 5) == 0) {
397413
StrHelper::strncpy(_prefs->node_name, &config[5], sizeof(_prefs->node_name));
398414
savePrefs();

0 commit comments

Comments
 (0)