--- /dev/null Thu Jan 1 00:00:00 1970 +++ /sys/src/libsec/port/dh.c Wed Jun 24 00:00:00 2026 @@ -0,0 +1,74 @@ +#include "os.h" +#include +#include + +mpint* +dh_new(DHstate *dh, mpint *p, mpint *q, mpint *g) +{ + mpint *pm1; + int n; + + memset(dh, 0, sizeof(*dh)); + if(mpcmp(g, mpone) <= 0) + return nil; + + n = mpsignif(p); + pm1 = mpnew(n); + mpsub(p, mpone, pm1); + dh->p = mpcopy(p); + dh->g = mpcopy(g); + dh->q = mpcopy(q != nil ? q : pm1); + dh->x = mpnew(mpsignif(dh->q)); + dh->y = mpnew(n); + for(;;){ + mpnrand(dh->q, genrandom, dh->x); + mpexp(dh->g, dh->x, dh->p, dh->y); + if(mpcmp(dh->y, mpone) > 0 && mpcmp(dh->y, pm1) < 0) + break; + } + mpfree(pm1); + + return dh->y; +} + +mpint* +dh_finish(DHstate *dh, mpint *y) +{ + mpint *k = nil; + + if(y == nil || dh->x == nil || dh->p == nil || dh->q == nil) + goto Out; + + /* y > 1 */ + if(mpcmp(y, mpone) <= 0) + goto Out; + + k = mpnew(mpsignif(dh->p)); + + /* y < p-1 */ + mpsub(dh->p, mpone, k); + if(mpcmp(y, k) >= 0){ +Bad: + mpfree(k); + k = nil; + goto Out; + } + + /* y**q % p == 1 if q < p-1 */ + if(mpcmp(dh->q, k) < 0){ + mpexp(y, dh->q, dh->p, k); + if(mpcmp(k, mpone) != 0) + goto Bad; + } + + mpexp(y, dh->x, dh->p, k); + +Out: + mpfree(dh->p); + mpfree(dh->q); + mpfree(dh->g); + mpfree(dh->x); + mpfree(dh->y); + memset(dh, 0, sizeof(*dh)); + return k; +} --- /n/sources/plan9/sys/include/libsec.h Sat Jun 14 00:00:00 2025 +++ /sys/include/libsec.h Wed Jun 24 00:00:00 2026 @@ -490,6 +490,19 @@ void curve25519_dh_new(uchar x[32], uchar y[32]); int curve25519_dh_finish(uchar x[32], uchar y[32], uchar z[32]); +/* Diffie-Hellman key exchange */ +typedef struct DHstate DHstate; +struct DHstate +{ + mpint *g; /* base g */ + mpint *p; /* large prime */ + mpint *q; /* subgroup prime */ + mpint *x; /* random secret */ + mpint *y; /* public key y = g**x % p */ +}; +mpint* dh_new(DHstate *dh, mpint *p, mpint *q, mpint *g); +mpint* dh_finish(DHstate *dh, mpint *y); + /* * TLS */ --- /n/sources/plan9/sys/include/ape/libsec.h Sat Jun 14 00:00:00 2025 +++ /sys/include/ape/libsec.h Wed Jun 24 00:00:00 2026 @@ -446,6 +446,19 @@ void curve25519_dh_new(uchar x[32], uchar y[32]); int curve25519_dh_finish(uchar x[32], uchar y[32], uchar z[32]); +/* Diffie-Hellman key exchange */ +typedef struct DHstate DHstate; +struct DHstate +{ + mpint *g; /* base g */ + mpint *p; /* large prime */ + mpint *q; /* subgroup prime */ + mpint *x; /* random secret */ + mpint *y; /* public key y = g**x % p */ +}; +mpint* dh_new(DHstate *dh, mpint *p, mpint *q, mpint *g); +mpint* dh_finish(DHstate *dh, mpint *y); + /* * TLS */ --- /dev/null Thu Jan 1 00:00:00 1970 +++ /sys/man/2/dh Wed Jun 24 00:00:00 2026 @@ -0,0 +1,64 @@ +.TH DH 2 +.SH NAME +dh_new, dh_finish \- ephemeral Diffie-Hellman key exchange +.SH SYNOPSIS +.B #include +.br +.B #include +.br +.B #include +.br +.B #include +.PP +.B +mpint* dh_new(DHstate *dh, mpint *p, mpint *q, mpint *g) +.PP +.B +mpint* dh_finish(DHstate *dh, mpint *y) +.SH DESCRIPTION +These routines implement ephemeral Diffie-Hellman key exchange +in the prime field given by the modulus +.I p +and generator +.IR g . +.PP +.I Dh_new +picks a fresh random secret exponent, records the exchange state in +.IR dh , +and returns the public value +.B g**x +mod +.IR p , +to be sent to the peer. +The +.I q +argument is the order of the subgroup generated by +.IR g . +When it is not nil it bounds the size of the secret exponent, and +.I dh_finish +later checks that the peer value lies in that subgroup. +.PP +.I Dh_finish +takes the peer's public value +.I y +and returns the shared secret +.B y**x +mod +.IR p , +then clears and frees the state held in +.IR dh . +The returned +.B mpint +is owned by the caller and should be freed with +.IR mpfree (2). +.SH SOURCE +.B /sys/src/libsec/port/dh.c +.SH "RETURN VALUE" +Both functions return nil on error, including a generator not +greater than one, a peer value out of range, or a failed subgroup check. +.SH "SEE ALSO" +.IR mp (2), +.IR rsa (2), +.IR ec (2) +.SH HISTORY +This implementation of Diffie-Hellman key exchange first appeared in 9front. --- /n/sources/plan9/sys/src/libsec/port/mkfile Sat Jun 14 00:00:00 2025 +++ /sys/src/libsec/port/mkfile Wed Jun 24 00:00:00 2026 @@ -22,7 +22,7 @@ hkdf.c\ tsmemcmp.c\ ccpoly.c\ - ecc.c jacobian.c secp256r1.c curve25519.c curve25519_dh.c\ + ecc.c jacobian.c secp256r1.c dh.c curve25519.c curve25519_dh.c\ CLEANFILES=secp256r1.c jacobian.c