commit 0bd0fc1f03b21c8ee7abe98c231f4561b673d941
parent 25bebdfd32b2534f1f3b6d558753203e091b6aa1
Author: MikoĊaj Lenczewski <mblenczewski@gmail.com>
Date: Sat, 18 Apr 2026 00:08:07 +0100
Add x25519 implementation
Diffstat:
8 files changed, 255 insertions(+), 16 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -1,5 +1,3 @@
-bin/
-
**/.*.swp
imgui.ini
tags
diff --git a/NOTES.txt b/NOTES.txt
@@ -2,3 +2,5 @@ https://datatracker.ietf.org/doc/html/rfc8446
https://tls13.xargs.org/
https://datatracker.ietf.org/doc/html/rfc2246
https://tls12.xargs.org/
+https://datatracker.ietf.org/doc/html/rfc7748
+https://x25519.xargs.org/
diff --git a/common.h b/common.h
@@ -8,17 +8,19 @@
#include <stdio.h>
#include <unistd.h>
+#include <sys/random.h>
+
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
static int
-get_socket(char *host, char *port, int passive)
+get_socket(char *host, char *port, int socktype, int protocol, int passive)
{
struct addrinfo hints = {
.ai_family = AF_UNSPEC,
- .ai_socktype = SOCK_STREAM,
- .ai_protocol = IPPROTO_TCP,
+ .ai_socktype = socktype,
+ .ai_protocol = protocol,
}, *addrinfo, *ptr;
int res;
@@ -46,21 +48,28 @@ get_socket(char *host, char *port, int passive)
break;
}
+ res = fd;
+
+ if (!ptr) {
+ fprintf(stderr, "getaddrinfo: failed to connect to %s:%s\n",
+ host, port);
+ }
+
freeaddrinfo(addrinfo);
- return fd;
+ return res;
}
static int
-get_client_socket(char *host, char *port)
+get_client_socket(char *host, char *port, int socktype, int protocol)
{
- return get_socket(host, port, 0);
+ return get_socket(host, port, socktype, protocol, 0);
}
static int
-get_server_socket(char *host, char *port)
+get_server_socket(char *host, char *port, int socktype, int protocol)
{
- int fd = get_socket(host, port, 1);
+ int fd = get_socket(host, port, socktype, protocol, 1);
if (fd < 0)
return fd;
diff --git a/curve25519.pdf b/curve25519.pdf
Binary files differ.
diff --git a/test-client.c b/test-client.c
@@ -14,7 +14,7 @@ main(int argc, char **argv)
char *host = argv[1], *port = argv[2];
- int sock = get_client_socket(host, port);
+ int sock = get_client_socket(host, port, SOCK_STREAM, IPPROTO_TCP);
if (sock < 0) {
fprintf(stderr, "Failed to connect to %s:%s\n", host, port);
exit(EXIT_FAILURE);
@@ -32,17 +32,22 @@ main(int argc, char **argv)
struct tls_session session;
tls_session_init(&session, TLS_VERSION_1_3, tlsbuf, sizeof tlsbuf);
- char client_privkey[32], client_pubkey[32];
+ struct x25519_point client_privkey, client_pubkey;
+ getrandom(&client_privkey.vs, sizeof client_privkey.vs, 0);
+ x25519_genpubkey(&client_pubkey, &client_privkey);
+
// TODO: generate keys using x25519
- tls_session_set_keys(&session, TLS_KEX_X25519,
- client_privkey, sizeof client_privkey,
- client_pubkey, sizeof client_pubkey);
+ tls_session_set_keys(&session, TLS_NAMED_GROUP_X25519,
+ client_privkey.vs, sizeof client_privkey.vs,
+ client_pubkey.vs, sizeof client_pubkey.vs);
+#if 0
char client_certificate[8192];
// TODO: generate certificate
tls_session_set_cert(&session,
client_certificate, sizeof client_certificate);
+#endif
// start client tls handshake
tls_session_client_handshake(&session);
diff --git a/test-server.c b/test-server.c
@@ -14,7 +14,7 @@ main(int argc, char **argv)
char *host = argv[1], *port = argv[2];
- int serv = get_server_socket(host, port);
+ int serv = get_server_socket(host, port, SOCK_STREAM, IPPROTO_TCP);
if (serv < 0) {
fprintf(stderr, "Failed to listen on %s:%s\n", host, port);
exit(EXIT_FAILURE);
diff --git a/tls.h b/tls.h
@@ -268,10 +268,17 @@ tls_session_send();
void
tls_session_send_commit();
+int
tls_session_pull();
+
+int
tls_session_push();
+
+int
tls_session_flush();
/* crypto - replace with real crypto library */
+#include "x25519.h"
+
#endif /* TLS_H */
diff --git a/x25519.h b/x25519.h
@@ -0,0 +1,218 @@
+// https://martin.kleppmann.com/papers/curve25519.pdf
+
+#include <stdint.h>
+#include <string.h>
+
+struct x25519_point {
+ uint8_t vs[32];
+};
+
+struct x25519_field_element {
+ int64_t vs[16]; // array of 16, 16-bit numbers (stored as 64-bit)
+};
+
+static inline void
+x25519_unpack(struct x25519_field_element *out, struct x25519_point const *in)
+{
+ for (size_t i = 0; i < 16; i++) {
+ out->vs[i] = in->vs[2*i] + ((int64_t) in->vs[(2*i) + 1] << 8);
+ }
+
+ out->vs[15] &= 0x7fff;
+}
+
+static inline void
+x25519_carry(struct x25519_field_element *elem)
+{
+ int64_t carry;
+ for (size_t i = 0; i < 16; i++) {
+ carry = elem->vs[i] >> 16;
+
+ elem->vs[i] -= carry << 16;
+
+ if (i < 15) {
+ elem->vs[i + 1] += carry;
+ } else { // i == 15
+ elem->vs[0] += 38 * carry;
+ }
+ }
+}
+
+static inline void
+x25519_fadd(struct x25519_field_element *out,
+ struct x25519_field_element const *a,
+ struct x25519_field_element const *b)
+{
+ for (size_t i = 0; i < 16; i++) {
+ out->vs[i] = a->vs[i] + b->vs[i];
+ }
+}
+
+static inline void
+x25519_fsub(struct x25519_field_element *out,
+ struct x25519_field_element const *a,
+ struct x25519_field_element const *b)
+{
+ for (size_t i = 0; i < 16; i++) {
+ out->vs[i] = a->vs[i] - b->vs[i];
+ }
+}
+
+static inline void
+x25519_fmul(struct x25519_field_element *out,
+ struct x25519_field_element const *a,
+ struct x25519_field_element const *b)
+{
+ int64_t product[31];
+ memset(product, 0, sizeof product); // TODO: is this elided?
+
+ for (size_t i = 0; i < 16; i++) {
+ for (size_t j = 0; j < 16; j++) {
+ product[i + j] = a->vs[i] * b->vs[j];
+ }
+ }
+
+ for (size_t i = 0; i < 15; i++) {
+ product[i] += 38 * product[i + 16];
+ }
+
+ memcpy(out->vs, product, sizeof out->vs);
+ x25519_carry(out);
+ x25519_carry(out);
+}
+
+static inline void
+x25519_finv(struct x25519_field_element *out,
+ struct x25519_field_element const *in)
+{
+ struct x25519_field_element c;
+ memcpy(&c, in, sizeof c);
+
+ for (int i = 253; i >= 0; i--) {
+ x25519_fmul(&c, &c, &c);
+
+ if (i != 2 && i != 4)
+ x25519_fmul(&c, &c, in);
+ }
+
+ memcpy(out, &c, sizeof c);
+}
+
+static inline void
+x25519_swap(struct x25519_field_element *p,
+ struct x25519_field_element *q,
+ size_t bit)
+{
+ int64_t c = ~(bit - 1);
+ for (size_t i = 0; i < 16; i++) {
+ int64_t t = c & (p->vs[i] ^ q->vs[i]);
+ p->vs[i] ^= t;
+ q->vs[i] ^= t;
+ }
+}
+
+static inline void
+x25519_pack(struct x25519_point *out, struct x25519_field_element const *in)
+{
+ int carry;
+ struct x25519_field_element m, t;
+ memcpy(&t, in, sizeof t);
+
+ // reduce module p
+ x25519_carry(&t); x25519_carry(&t); x25519_carry(&t);
+
+ for (size_t j = 0; j < 2; j++) {
+ m.vs[0] = t.vs[0] - 0xffed;
+ for (size_t i = 1; i < 15; i++) {
+ m.vs[i] = t.vs[i] - 0xffff - ((m.vs[i - 1] >> 16) & 1);
+ m.vs[i - 1] &= 0xffff;
+ }
+
+ m.vs[15] = t.vs[15] - 0x7fff - ((m.vs[14] >> 16) & 1);
+ carry = (m.vs[15] >> 16) & 1;
+ m.vs[14] &= 0xffff;
+
+ x25519_swap(&t, &m, 1 - carry);
+ }
+
+ for (size_t i = 0; i < 16; i++) {
+ out->vs[2*i] = t.vs[i] & 0xff;
+ out->vs[(2*i) + 1] = t.vs[i] >> 8;
+ }
+}
+
+static inline void
+x25519_scalarmult(struct x25519_point *restrict out,
+ struct x25519_point const *restrict scalar,
+ struct x25519_point const *restrict point)
+{
+ static const struct x25519_field_element _121665 = {
+ .vs = { 0xdb41, 1 }
+ };
+
+ uint8_t clamped[32];
+ int64_t bit;
+
+ struct x25519_field_element a, b, c, d, e, f, x;
+
+ memcpy(clamped, scalar, sizeof clamped);
+ clamped[0] &= 0xf8;
+ clamped[31] = (clamped[31] & 0x7f) | 0x40;
+
+ x25519_unpack(&x, point);
+
+ memcpy(&b, &x, sizeof b);
+ memset(&d, 0, sizeof d);
+ memset(&c, 0, sizeof c);
+ memset(&a, 0, sizeof a);
+ a.vs[0] = d.vs[0] = 1;
+
+ for (int i = 254; i >= 0; i--) {
+ bit = (clamped[i >> 3] >> (i & 7)) & 1;
+
+ x25519_swap(&a, &b, bit);
+ x25519_swap(&c, &d, bit);
+
+ x25519_fadd(&e, &a, &c);
+ x25519_fsub(&a, &a, &c);
+ x25519_fadd(&c, &b, &d);
+ x25519_fsub(&b, &b, &d);
+ x25519_fmul(&d, &e, &f);
+ x25519_fmul(&f, &a, &a);
+ x25519_fmul(&a, &c, &a);
+ x25519_fmul(&c, &b, &e);
+ x25519_fadd(&e, &a, &c);
+ x25519_fsub(&a, &a, &c);
+ x25519_fmul(&b, &a, &a);
+ x25519_fsub(&c, &d, &f);
+ x25519_fmul(&a, &c, &_121665);
+ x25519_fadd(&a, &a, &d);
+ x25519_fmul(&c, &c, &a);
+ x25519_fmul(&a, &d, &f);
+ x25519_fmul(&d, &b, &x);
+ x25519_fmul(&b, &e, &e);
+
+ x25519_swap(&a, &b, bit);
+ x25519_swap(&c, &d, bit);
+ }
+
+ x25519_finv(&c, &c);
+ x25519_fmul(&a, &a, &c);
+ x25519_pack(out, &a);
+}
+
+static inline void
+x25519_genpubkey(struct x25519_point *restrict pk,
+ struct x25519_point const *restrict sk)
+{
+ static const struct x25519_point _9 = { .vs = {9} };
+ x25519_scalarmult(pk, sk, &_9);
+}
+
+static inline void
+x25519(struct x25519_point *restrict out,
+ struct x25519_point const *restrict pk,
+ struct x25519_point const *restrict sk)
+{
+ x25519_scalarmult(out, sk, pk);
+}