~moody/libdp9ik

4efa26b9426a92222d1bc9478cffbaec6f2c18d7 — Jacob Moody 6 months ago a2a95b2 master
Progress made towards authpak_new and authpak_finish
The functions do not work currently
8 files changed, 522 insertions(+), 16 deletions(-)

M auth.go
M crypt.go
A decaf.go
A edwards.go
M p9any.go
M p9any_test.go
M spake2ee.go
M ticket.go
M auth.go => auth.go +100 -0
@@ 1,7 1,9 @@
package libdp9ik

import (
	"crypto/rand"
	"crypto/sha256"
	"errors"
	"math/big"

	"golang.org/x/crypto/hkdf"


@@ 67,3 69,101 @@ func (ak *AuthKey) AuthpakHash(u []byte) error {

	return nil
}

type PAKPriv struct {
	Isclient bool
	X        [Pakxlen]byte
	Y        [Pakylen]byte
}

func authpak_new(ak *AuthKey, y []byte, isclient int) (*PAKPriv, error) {
	p := &PAKPriv{}
	p.Isclient = (isclient != 0)

	Y := big.NewInt(0)

	PX := big.NewInt(0)
	PY := big.NewInt(0)
	PZ := big.NewInt(0)
	PT := big.NewInt(0)

	n := 0
	if p.Isclient == false {
		n += Pakplen
	}
	move := func(b *big.Int) {
		b.FillBytes(ak.Pakhash[n : n+Pakslen])
		n += Pakslen
	}
	move(PX)
	move(PY)
	move(PZ)
	move(PT)

	c := GPAKCurve
	X, err := rand.Int(rand.Reader, c.P)
	if err != nil {
		return nil, err
	}

	Spake2ee_1(c.P, c.A, c.D, X, c.X, c.Y, PX, PY, PZ, PT, Y)
	X.FillBytes(p.X[:])
	Y.FillBytes(p.Y[:])

	copy(y, p.Y[:])

	return p, nil
}

func (p *PAKPriv) authpak_finish(ak *AuthKey, y []byte) error {
	info := []byte("Plan 9 AuthPAK Key")
	z := make([]byte, Pakslen)

	X := big.NewInt(0)
	Y := big.NewInt(0)
	Z := big.NewInt(0)
	ok := big.NewInt(0)

	PX := big.NewInt(0)
	PY := big.NewInt(0)
	PZ := big.NewInt(0)
	PT := big.NewInt(0)

	n := 0
	if p.Isclient {
		n += Pakplen
	}
	move := func(b *big.Int) {
		b.FillBytes(ak.Pakhash[n : n+Pakslen])
		n += Pakslen
	}
	move(PX)
	move(PY)
	move(PZ)
	move(PT)

	X.SetBytes(p.X[:])
	Y.SetBytes(y[:Pakylen])

	c := GPAKCurve
	Spake2ee_2(c.P, c.A, c.D, PX, PY, PZ, PT, X, Y, ok, Z)

	if ok.Cmp(big.NewInt(0)) == 0 {
		return errors.New("spake2ee_2 failed")
	}

	Z.FillBytes(z)

	sha := sha256.New()
	if p.Isclient {
		sha.Write(p.Y[:])
		sha.Write(y[:Pakylen])
	} else {
		sha.Write(y[:Pakylen])
		sha.Write(p.Y[:])
	}
	salt := sha.Sum([]byte{})
	hr := hkdf.New(sha256.New, z[:], salt[:], info)
	_, err := hr.Read(ak.Pakkey[:])
	return err
}

M crypt.go => crypt.go +2 -2
@@ 29,8 29,8 @@ const (
)

//GPAKCurve is to be used as readonly
var GPAKCurve *PAKCurve = func() *PAKCurve {
	c := &PAKCurve{
var GPAKCurve PAKCurve = func() PAKCurve {
	c := PAKCurve{
		&big.Int{},
		&big.Int{},
		&big.Int{},

A decaf.go => decaf.go +110 -0
@@ 0,0 1,110 @@
package libdp9ik

import "math/big"
import "unsafe"

func decaf_neg(p *big.Int, n *big.Int, r *big.Int) {
	m := big.NewInt(0)
	modsub(big.NewInt(0), r, p, m)
	tmp1 := big.NewInt(0)
	tmp1.Sub(p, big.NewInt(1))
	tmp1.Rsh(tmp1, 1)
	c := -tmp1.Cmp(n)
	if (c >> (unsafe.Sizeof(c)*8 - 1)) != 0 {
		r.Set(m)
	}
}

func decaf_encode(p *big.Int, a *big.Int, d *big.Int, X *big.Int, Y *big.Int, Z *big.Int, T *big.Int, s *big.Int) {
	u := big.NewInt(0)
	r := big.NewInt(0)
	tmp1 := big.NewInt(0)
	tmp2 := big.NewInt(0)
	tmp3 := big.NewInt(0)
	modsub(a, d, p, tmp3)
	tmp4 := big.NewInt(0)
	modadd(Z, Y, p, tmp4)
	modmul(tmp3, tmp4, p, tmp2)
	tmp4.SetInt64(0)
	modsub(Z, Y, p, tmp4)
	modmul(tmp2, tmp4, p, tmp1)
	misqrt(tmp1, p, r)
	tmp1.SetInt64(0)
	modsub(a, d, p, tmp1)
	modmul(tmp1, r, p, u)
	tmp1.SetInt64(0)
	tmp4.SetInt64(0)
	modadd(u, u, p, tmp4)
	modmul(tmp4, Z, p, tmp1)
	modsub(big.NewInt(0), tmp1, p, tmp1)
	decaf_neg(p, tmp1, r)
	tmp1.SetInt64(0)
	tmp4.SetInt64(0)
	tmp2.SetInt64(0)
	tmp3.SetInt64(0)
	modmul(a, Z, p, tmp3)
	modmul(tmp3, X, p, tmp2)
	tmp3.SetInt64(0)
	tmp5 := big.NewInt(0)
	modmul(d, Y, p, tmp5)
	modmul(tmp5, T, p, tmp3)
	modsub(tmp2, tmp3, p, tmp2)
	modmul(r, tmp2, p, tmp4)
	modadd(tmp4, Y, p, tmp4)
	modmul(u, tmp4, p, tmp1)
	tmp4.SetInt64(0)
	tmp4.ModInverse(a, p)
	modmul(tmp1, tmp4, p, s)
	decaf_neg(p, s, s)
}

func decaf_decode(p *big.Int, a *big.Int, d *big.Int, s *big.Int, ok *big.Int, X *big.Int, Y *big.Int, Z *big.Int, T *big.Int) {
	w := big.NewInt(0)
	v := big.NewInt(0)
	u := big.NewInt(0)
	ss := big.NewInt(0)
	tmp1 := big.NewInt(0)
	tmp1.Sub(p, big.NewInt(1))
	tmp1.Rsh(tmp1, 1)
	if tmp1.Cmp(s) > 0 {
		ok.SetInt64(0)
	} else {
		modmul(s, s, p, ss)
		modmul(a, ss, p, Z)
		modadd(big.NewInt(1), Z, p, Z)
		modmul(Z, Z, p, u)
		tmp2 := big.NewInt(0)
		tmp3 := big.NewInt(0)
		tmp4 := big.NewInt(0)
		tmp4.SetUint64(4)
		modmul(tmp4, d, p, tmp3)
		modmul(tmp3, ss, p, tmp2)
		modsub(u, tmp2, p, u)
		modmul(u, ss, p, v)
		if big.NewInt(0).Cmp(v) == 0 {
			ok.SetInt64(1)
		} else {
			ok.ModSqrt(v, p)
			if big.NewInt(0).Cmp(ok) != 0 {
				v.ModInverse(ok, p)
				ok.SetInt64(1)
			}
		}
		if big.NewInt(0).Cmp(ok) != 0 {
			tmp5 := big.NewInt(0)
			modmul(u, v, p, tmp5)
			decaf_neg(p, tmp5, v)
			tmp5.SetInt64(0)
			modmul(v, s, p, tmp5)
			tmp6 := big.NewInt(0)
			modsub(big.NewInt(2), Z, p, tmp6)
			modmul(tmp5, tmp6, p, w)
			if big.NewInt(0).Cmp(s) == 0 {
				modadd(w, big.NewInt(1), p, w)
			}
			modadd(s, s, p, X)
			modmul(w, Z, p, Y)
			modmul(w, X, p, T)
		}
	}
}

A edwards.go => edwards.go +90 -0
@@ 0,0 1,90 @@
package libdp9ik

import "math/big"

func mpsel(c int, b1 *big.Int, b2 *big.Int, r *big.Int) {
	if c != 0 {
		r.Set(b1)
	} else {
		r.Set(b2)
	}
}

func edwards_sel(s *big.Int, X1 *big.Int, Y1 *big.Int, Z1 *big.Int, T1 *big.Int, X2 *big.Int, Y2 *big.Int, Z2 *big.Int, T2 *big.Int, X3 *big.Int, Y3 *big.Int, Z3 *big.Int, T3 *big.Int) {
	c := s.Cmp(big.NewInt(0))
	mpsel(c, X1, X2, X3)
	mpsel(c, Y1, Y2, Y3)
	mpsel(c, Z1, Z2, Z3)
	mpsel(c, T1, T2, T3)
}

func edwards_new(x *big.Int, y *big.Int, z *big.Int, t *big.Int, X *big.Int, Y *big.Int, Z *big.Int, T *big.Int) {
	X.Set(x)
	Y.Set(y)
	Z.Set(z)
	T.Set(t)
}

func edwards_add(p *big.Int, a *big.Int, d *big.Int, X1 *big.Int, Y1 *big.Int, Z1 *big.Int, T1 *big.Int, X2 *big.Int, Y2 *big.Int, Z2 *big.Int, T2 *big.Int, X3 *big.Int, Y3 *big.Int, Z3 *big.Int, T3 *big.Int) {
	H := big.NewInt(0)
	G := big.NewInt(0)
	F := big.NewInt(0)
	E := big.NewInt(0)
	D := big.NewInt(0)
	C := big.NewInt(0)
	B := big.NewInt(0)
	A := big.NewInt(0)
	modmul(X1, X2, p, A)
	modmul(Y1, Y2, p, B)
	tmp1 := big.NewInt(0)
	modmul(d, T1, p, tmp1)
	modmul(tmp1, T2, p, C)
	modmul(Z1, Z2, p, D)
	tmp1.SetInt64(1)
	modadd(X1, Y1, p, tmp1)
	tmp2 := big.NewInt(0)
	modadd(X2, Y2, p, tmp2)
	modsub(E, A, p, E)
	modsub(E, B, p, E)
	modsub(D, C, p, F)
	modadd(D, C, p, G)
	modmul(a, A, p, H)
	modsub(B, H, p, H)
	modmul(E, F, p, X3)
	modmul(G, H, p, Y3)
	modmul(F, G, p, Z3)
	modmul(E, H, p, T3)
}

func edwards_scale(p *big.Int, a *big.Int, d *big.Int, s *big.Int, X1 *big.Int, Y1 *big.Int, Z1 *big.Int, T1 *big.Int, X3 *big.Int, Y3 *big.Int, Z3 *big.Int, T3 *big.Int) {
	j := big.NewInt(0)
	k := big.NewInt(0)
	T4 := big.NewInt(0)
	Z4 := big.NewInt(0)
	Y4 := big.NewInt(0)
	X4 := big.NewInt(0)
	T2 := big.NewInt(0)
	Z2 := big.NewInt(0)
	Y2 := big.NewInt(0)
	X2 := big.NewInt(0)
	edwards_new(X1, Y1, Z1, T1, X2, Y2, Z2, T2)
	edwards_new(big.NewInt(0), big.NewInt(1), big.NewInt(1), big.NewInt(0), X4, Y4, Z4, T4)
	tmp1 := big.NewInt(0)
	tmp1.Mod(s, big.NewInt(2))
	edwards_sel(tmp1, X2, Y2, Z2, T2, X4, Y4, Z4, T4, X3, Y3, Z3, T3)
	k.Rsh(s, 1)
	j.Rsh(p, 1)
	for {
		if j.Cmp(big.NewInt(0)) != 0 {
			edwards_add(p, a, d, X2, Y2, Z2, T2, X2, Y2, Z2, T2, X2, Y2, Z2, T2)
			edwards_add(p, a, d, X2, Y2, Z2, T2, X3, Y3, Z3, T3, X4, Y4, Z4, T4)
			tmp2 := big.NewInt(0)
			tmp2.Mod(k, big.NewInt(2))
			edwards_sel(tmp2, X4, Y4, Z4, T4, X3, Y3, Z3, T3, X3, Y3, Z3, T3)
			k.Rsh(k, 1)
			j.Rsh(j, 1)
		} else {
			break
		}
	}
}

M p9any.go => p9any.go +56 -7
@@ 5,6 5,7 @@ import (
	"errors"
	"fmt"
	"io"
	"log"
	"strings"
)



@@ 33,7 34,9 @@ const (
	Pakxlen    = Pakslen
	Pakylen    = Pakslen

	Tickreqlen = 3*Anamelen + Challen + Domlen + 1
	Tickreqlen    = 3*Anamelen + Challen + Domlen + 1
	Maxticketlen  = 12 + Challen + 2*Anamelen + Noncelen + 16
	Maxauthentlen = 12 + Challen + Noncelen + 16
)

var (


@@ 41,7 44,40 @@ var (
	ErrNoDom   = errors.New("server did not offer a dom")
)

func P9any(fd io.ReadWriter, user, pass string) (*AuthInfo, error) {
func Getastickets(asfd io.ReadWriter, ak *AuthKey, tr *TicketReq, y []byte, tbuf []byte) error {
	if y != nil {
		//dp9ik
		tr.Which = AuthPAK
		tr.asrequest(asfd)
		_, err := asfd.Write(y[:Pakylen])
		if err != nil {
			return err
		}
		p, err := authpak_new(ak, tbuf, 1)
		if err != nil {
			return err
		}
		_, err = asfd.Write(tbuf[:Pakylen])
		if err != nil {
			return err
		}
		err = asrdresp(asfd, tbuf)
		if err != nil {
			return err
		}
		copy(y, tbuf[:Pakylen])
		err = p.authpak_finish(ak, tbuf[Pakylen:])
		if err != nil {
			return err
		}
	}
	tr.Which = AuthTreq

	return nil
}

func P9any(fd io.ReadWriter, asfd io.ReadWriter, user, pass string) (*AuthInfo, error) {
	y := make([]byte, Pakylen)
	buf := make([]byte, 1024)
	_, err := fd.Read(buf)
	if err != nil {


@@ 86,13 122,13 @@ func P9any(fd io.ReadWriter, user, pass string) (*AuthInfo, error) {
	if proto == dp9ik {
		n += Pakylen
	}
	buf = make([]byte, n)
	_, err = fd.Read(buf)
	trbuf := make([]byte, n)
	_, err = fd.Read(trbuf)
	if err != nil {
		return nil, err
	}
	tr := &TicketReq{}
	tr.UnmarshalBinary(buf)
	tr.UnmarshalBinary(trbuf)

	ak := &AuthKey{}
	ak.Passtokey(pass)


@@ 103,8 139,21 @@ func P9any(fd io.ReadWriter, user, pass string) (*AuthInfo, error) {
	copy(tr.Hostid[:], []byte(user))
	copy(tr.Uid[:], []byte(user))

	y := make([]byte, Pakylen)
	copy(y, buf[Tickreqlen:])
	tbuf := make([]byte, 2*Maxticketlen+Maxauthentlen+Pakylen)
	if proto == dp9ik {
		copy(y[:Pakylen], trbuf[Tickreqlen:])
		err := Getastickets(asfd, ak, tr, y, tbuf)
		if err != nil {
			return nil, err
		}
	}

	t := &Ticket{}
	t.UnmarshalBinary(tbuf)
	log.Println(t.Num, AuthTc)
	if t.Num != AuthTc {
		log.Fatal("disaster, password mismatch")
	}

	return nil, nil
}

M p9any_test.go => p9any_test.go +15 -7
@@ 1,20 1,28 @@
package libdp9ik_test

import (
	//"net"
	auth "git.sr.ht/~moody/libdp9ik"
	"net"
	"testing"
	//auth "git.sr.ht/~moody/libdp9ik"
)

const (
	rcpuPort  = "17019"
	cpuServer = "192.168.1.6"
	authPort  = "567"
)

func TestP9any(t *testing.T) {
	//fd, err := net.Dial("tcp", cpuServer+":"+rcpuPort)
	//if err != nil {
	//	t.Fatal(err)
	//}
	//auth.P9any(fd, "user", "password")
	fd, err := net.Dial("tcp", cpuServer+":"+rcpuPort)
	if err != nil {
		t.Fatal(err)
	}
	tfd, err := net.Dial("tcp", cpuServer+":"+authPort)
	if err != nil {
		t.Fatal(err)
	}
	_, err = auth.P9any(fd, tfd, "user", "password")
	if err != nil {
		t.Fatal(err)
	}
}

M spake2ee.go => spake2ee.go +29 -0
@@ 30,3 30,32 @@ func Spake2ee_h2P(p *big.Int, a *big.Int, d *big.Int, h *big.Int, PX *big.Int, P
	tmp3.Mod(h, p)
	elligator2(p, a, d, n, tmp3, PX, PY, PZ, PT)
}

func Spake2ee_1(p *big.Int, a *big.Int, d *big.Int, x *big.Int, GX *big.Int, GY *big.Int, PX *big.Int, PY *big.Int, PZ *big.Int, PT *big.Int, y *big.Int) {
	T := big.NewInt(0)
	Z := big.NewInt(0)
	Y := big.NewInt(0)
	X := big.NewInt(0)
	tmp1 := big.NewInt(0)
	modmul(GX, GY, p, tmp1)
	edwards_scale(p, a, d, x, GX, GY, big.NewInt(1), tmp1, X, Y, Z, T)
	edwards_add(p, a, d, X, Y, Z, T, PX, PY, PZ, PT, X, Y, Z, T)
	decaf_encode(p, a, d, X, Y, Z, T, y)
}

func Spake2ee_2(p *big.Int, a *big.Int, d *big.Int, PX *big.Int, PY *big.Int, PZ *big.Int, PT *big.Int, x *big.Int, y *big.Int, ok *big.Int, z *big.Int) {
	T := big.NewInt(0)
	Z := big.NewInt(0)
	Y := big.NewInt(0)
	X := big.NewInt(0)
	decaf_decode(p, a, d, y, ok, X, Y, Z, T)
	if ok.Cmp(big.NewInt(0)) != 0 {
		tmp1 := big.NewInt(0)
		modsub(big.NewInt(0), PX, p, tmp1)
		tmp2 := big.NewInt(0)
		modsub(big.NewInt(0), PT, p, tmp2)
		edwards_add(p, a, d, X, Y, Z, T, tmp1, PY, PZ, tmp2, X, Y, Z, T)
		edwards_scale(p, a, d, x, X, Y, Z, T, X, Y, Z, T)
		decaf_encode(p, a, d, X, Y, Z, T, z)
	}
}

M ticket.go => ticket.go +120 -0
@@ 1,10 1,37 @@
package libdp9ik

import (
	"errors"
	"fmt"
	"io"
	"strconv"
	"strings"
)

const (
	AuthTreq = iota + 1
	AuthChal
	AuthPass
	AuthOK
	AuthErr
	AuthMod
	AuthApop
	_
	AuthOKvar
	AuthChap
	AuthMSchap
	AuthCram
	AuthHttp
	AuthVNC = 14
	AuthPAK = 19
	AuthTs  = iota + 64
	AuthTc
	AuthAs
	AuthAc
	AuthTp
	AuthHr
)

type TicketReq struct {
	Which   byte
	Authid  [Anamelen]byte


@@ 43,6 70,25 @@ func (t *TicketReq) UnmarshalBinary(data []byte) error {
	return nil
}

func (t *TicketReq) MarshalBinary() ([]byte, error) {
	size := 1 + Anamelen + Domlen + Challen + Anamelen + Anamelen
	out := make([]byte, size)
	n := 0

	out[0] = t.Which
	n++
	move := func(src []byte, width int) {
		copy(out[n:n+width], src)
		n += width
	}
	move(t.Authid[:], Anamelen)
	move(t.Authdom[:], Domlen)
	move(t.Chal[:], Challen)
	move(t.Hostid[:], Anamelen)
	move(t.Uid[:], Anamelen)
	return out, nil
}

type Ticket struct {
	Num  byte           /* replay protection */
	Chal [Challen]byte  /* server challenge */


@@ 52,3 98,77 @@ type Ticket struct {

	Form byte /* (not transmitted) format (0 = des, 1 = ccpoly) */
}

func (t *Ticket) UnmarshalBinary(data []byte) error {
	n := 0
	move := func(dst []byte, width int) {
		copy(dst, data[n:n+width])
		n += width
	}
	t.Num = data[n]
	n++
	move(t.Chal[:], Challen)
	move(t.Cuid[:], Anamelen)
	move(t.Suid[:], Anamelen)
	move(t.Key[:], Noncelen)
	return nil
}

func asrdresp(fd io.ReadWriter, buf []byte) error {
	errmsg := make([]byte, 64)
	_, err := fd.Read(buf[:1])
	if err != nil {
		return err
	}
	switch buf[0] {
	case AuthOK:
		_, err = fd.Read(buf)
		return err
	case AuthErr:
		_, err = fd.Read(errmsg)
		if err != nil {
			return err
		}
		return errors.New(string(errmsg))
	case AuthOKvar:
		_, err = fd.Read(errmsg[:5])
		if err != nil {
			return err
		}
		n, err := strconv.Atoi(string(errmsg[:5]))
		if err != nil {
			return err
		}
		_, err = fd.Read(buf[:n])
		if err != nil {
			return err
		}
	default:
		return errors.New("unknown Which from auth server")
	}
	return nil
}

func (tr *TicketReq) asgetticket(asfd io.ReadWriter, tbuf []byte) error {
	err := tr.asrequest(asfd)
	if err != nil {
		return err
	}
	err = asrdresp(asfd, tbuf)
	if err != nil {
		return err
	}

	//According to libauthsrv/ticket.c:20 in drawterm this is suppsoed
	//to try and pull some more data, however it seems like in the
	//case of a client(our case) it is never needed so we simply ignore
	//it as being a dp9ik server is outside our scope

	return nil
}

func (tr *TicketReq) asrequest(asfd io.ReadWriter) error {
	b, _ := tr.MarshalBinary()
	_, err := asfd.Write(b)
	return err
}