- vendor orlp/ed25519 (lib/ed25519, MIT — the exact lib MeshCore uses) for Curve25519 ECDH key exchange; switch identity/advert signing to it (same seed -> same pubkey, so peers still recognise us; sigs are byte-identical) - store pubkeys from heard MeshCore adverts (contacts table) so a DM's 1-byte src_hash can be resolved to a full sender key - decode PAYLOAD_TYPE_TXT_MSG: ECDH shared secret, HMAC-SHA256 MAC check, AES-128-ECB decrypt, parse timestamp+text, drive the message modal - verified end-to-end: real MeshCore node -> "hello from HP Pro Desk" decoded Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
70 lines
2.1 KiB
C
70 lines
2.1 KiB
C
#include "ed_25519.h"
|
|
#include "ge.h"
|
|
#include "sc.h"
|
|
#include "sha512.h"
|
|
|
|
|
|
/* see http://crypto.stackexchange.com/a/6215/4697 */
|
|
void ed25519_add_scalar(unsigned char *public_key, unsigned char *private_key, const unsigned char *scalar) {
|
|
const unsigned char SC_1[32] = {1}; /* scalar with value 1 */
|
|
|
|
unsigned char n[32];
|
|
ge_p3 nB;
|
|
ge_p1p1 A_p1p1;
|
|
ge_p3 A;
|
|
ge_p3 public_key_unpacked;
|
|
ge_cached T;
|
|
|
|
sha512_context hash;
|
|
unsigned char hashbuf[64];
|
|
|
|
int i;
|
|
|
|
/* copy the scalar and clear highest bit */
|
|
for (i = 0; i < 31; ++i) {
|
|
n[i] = scalar[i];
|
|
}
|
|
n[31] = scalar[31] & 127;
|
|
|
|
/* private key: a = n + t */
|
|
if (private_key) {
|
|
sc_muladd(private_key, SC_1, n, private_key);
|
|
|
|
// https://github.com/orlp/ed25519/issues/3
|
|
sha512_init(&hash);
|
|
sha512_update(&hash, private_key + 32, 32);
|
|
sha512_update(&hash, scalar, 32);
|
|
sha512_final(&hash, hashbuf);
|
|
for (i = 0; i < 32; ++i) {
|
|
private_key[32 + i] = hashbuf[i];
|
|
}
|
|
}
|
|
|
|
/* public key: A = nB + T */
|
|
if (public_key) {
|
|
/* if we know the private key we don't need a point addition, which is faster */
|
|
/* using a "timing attack" you could find out wether or not we know the private
|
|
key, but this information seems rather useless - if this is important pass
|
|
public_key and private_key seperately in 2 function calls */
|
|
if (private_key) {
|
|
ge_scalarmult_base(&A, private_key);
|
|
} else {
|
|
/* unpack public key into T */
|
|
ge_frombytes_negate_vartime(&public_key_unpacked, public_key);
|
|
fe_neg(public_key_unpacked.X, public_key_unpacked.X); /* undo negate */
|
|
fe_neg(public_key_unpacked.T, public_key_unpacked.T); /* undo negate */
|
|
ge_p3_to_cached(&T, &public_key_unpacked);
|
|
|
|
/* calculate n*B */
|
|
ge_scalarmult_base(&nB, n);
|
|
|
|
/* A = n*B + T */
|
|
ge_add(&A_p1p1, &nB, &T);
|
|
ge_p1p1_to_p3(&A, &A_p1p1);
|
|
}
|
|
|
|
/* pack public key */
|
|
ge_p3_tobytes(public_key, &A);
|
|
}
|
|
}
|