From eaa034204a64e84bd88a318a806363e72b0e99bb Mon Sep 17 00:00:00 2001 From: adnano Date: Fri, 25 Sep 2020 20:22:48 -0400 Subject: [PATCH] Add method to check if hostname is found in KnownHosts --- README.md | 3 ++- client.go | 4 ++-- examples/client/client.go | 5 +---- tofu.go | 31 ++++++++++++++++++++++++++----- 4 files changed, 31 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 56712e9..33eebd7 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,8 @@ A quick overview of the Gemini protocol: The way this is implemented in this package is like so: 1. Client makes a request with `NewRequest`. The client then sends the request - with `Send(*Request) (*Response, error)`. + with `Send(*Request) (*Response, error)`. The client can optionally verify + the server certificates with `VerifyCertificate(*x509.Certificate, *Request)` 2. Server recieves the request and constructs a response. The server calls the `Serve(*ResponseWriter, *Request)` method on the `Handler` field. The handler writes the response. The server then closes diff --git a/client.go b/client.go index 4a4f32c..dd38f67 100644 --- a/client.go +++ b/client.go @@ -166,7 +166,7 @@ func (resp *Response) read(r *bufio.Reader) error { type Client struct { // VerifyCertificate, if not nil, will be called to verify the server certificate. // If error is not nil, the connection will be aborted. - VerifyCertificate func(cert *x509.Certificate) error + VerifyCertificate func(cert *x509.Certificate, req *Request) error } // Send sends a Gemini request and returns a Gemini response. @@ -180,7 +180,7 @@ func (c *Client) Send(req *Request) (*Response, error) { if err != nil { return err } - return c.VerifyCertificate(cert) + return c.VerifyCertificate(cert, req) }, } conn, err := tls.Dial("tcp", req.Host, config) diff --git a/examples/client/client.go b/examples/client/client.go index 353359c..89bdb12 100644 --- a/examples/client/client.go +++ b/examples/client/client.go @@ -15,10 +15,7 @@ import ( var ( client = &gemini.Client{ - VerifyCertificate: func(cert *x509.Certificate) error { - // if gemini.Fingerprint(cert) != expected { - // return errors.New("invalid server certificate") - // } + VerifyCertificate: func(cert *x509.Certificate, req *gemini.Request) error { return nil }, } diff --git a/tofu.go b/tofu.go index a40ae38..f3d4bea 100644 --- a/tofu.go +++ b/tofu.go @@ -10,6 +10,7 @@ import ( "io" "strconv" "strings" + "time" ) // Errors. @@ -17,16 +18,31 @@ var ( ErrInvalidKnownHosts = errors.New("gemini: invalid known hosts") ) +// KnownHosts represents a list of known hosts. +type KnownHosts []KnownHost + +// Has reports whether the given hostname and certificate are in the list. +func (k KnownHosts) Has(hostname string, cert *x509.Certificate) bool { + now := time.Now().Unix() + fingerprint := Fingerprint(cert) + for i := range k { + if k[i].Expires < now && k[i].Hostname == hostname && k[i].Fingerprint == fingerprint { + return true + } + } + return false +} + // KnownHost represents a known host. type KnownHost struct { Hostname string // e.g. gemini.circumlunar.space - Algorithm string // fingerprint algorithm + Algorithm string // fingerprint algorithm e.g. SHA-512 Fingerprint string // fingerprint in hexadecimal, with ':' between each octet - NotAfter int64 // unix time of certificate notAfter date + Expires int64 // unix time of certificate notAfter date } // ParseKnownHosts parses and returns a list of known hosts from the provided io.Reader. -func ParseKnownHosts(r io.Reader) ([]KnownHost, error) { +func ParseKnownHosts(r io.Reader) (KnownHosts, error) { hosts := []KnownHost{} scanner := bufio.NewScanner(r) @@ -41,7 +57,7 @@ func ParseKnownHosts(r io.Reader) ([]KnownHost, error) { hostname := parts[0] algorithm := parts[1] fingerprint := parts[2] - notAfter, err := strconv.ParseInt(parts[3], 10, 0) + expires, err := strconv.ParseInt(parts[3], 10, 0) if err != nil { return nil, ErrInvalidKnownHosts } @@ -50,13 +66,18 @@ func ParseKnownHosts(r io.Reader) ([]KnownHost, error) { Hostname: hostname, Algorithm: algorithm, Fingerprint: fingerprint, - NotAfter: notAfter, + Expires: expires, }) } return hosts, nil } +// AppendKnownHost appends the host to the provided io.Writer. +func AppendKnownHost(host KnownHost, w io.Writer) error { + return nil +} + // Fingerprint returns the SHA-512 fingerprint of the provided certificate. func Fingerprint(cert *x509.Certificate) string { sum512 := sha512.Sum512(cert.Raw)