Add method to check if hostname is found in KnownHosts

This commit is contained in:
adnano 2020-09-25 20:22:48 -04:00
parent 53d84882ea
commit eaa034204a
4 changed files with 31 additions and 12 deletions

View File

@ -33,7 +33,8 @@ A quick overview of the Gemini protocol:
The way this is implemented in this package is like so: The way this is implemented in this package is like so:
1. Client makes a request with `NewRequest`. The client then sends the request 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. 2. Server recieves the request and constructs a response.
The server calls the `Serve(*ResponseWriter, *Request)` method on the The server calls the `Serve(*ResponseWriter, *Request)` method on the
`Handler` field. The handler writes the response. The server then closes `Handler` field. The handler writes the response. The server then closes

View File

@ -166,7 +166,7 @@ func (resp *Response) read(r *bufio.Reader) error {
type Client struct { type Client struct {
// VerifyCertificate, if not nil, will be called to verify the server certificate. // VerifyCertificate, if not nil, will be called to verify the server certificate.
// If error is not nil, the connection will be aborted. // 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. // 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 { if err != nil {
return err return err
} }
return c.VerifyCertificate(cert) return c.VerifyCertificate(cert, req)
}, },
} }
conn, err := tls.Dial("tcp", req.Host, config) conn, err := tls.Dial("tcp", req.Host, config)

View File

@ -15,10 +15,7 @@ import (
var ( var (
client = &gemini.Client{ client = &gemini.Client{
VerifyCertificate: func(cert *x509.Certificate) error { VerifyCertificate: func(cert *x509.Certificate, req *gemini.Request) error {
// if gemini.Fingerprint(cert) != expected {
// return errors.New("invalid server certificate")
// }
return nil return nil
}, },
} }

31
tofu.go
View File

@ -10,6 +10,7 @@ import (
"io" "io"
"strconv" "strconv"
"strings" "strings"
"time"
) )
// Errors. // Errors.
@ -17,16 +18,31 @@ var (
ErrInvalidKnownHosts = errors.New("gemini: invalid known hosts") 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. // KnownHost represents a known host.
type KnownHost struct { type KnownHost struct {
Hostname string // e.g. gemini.circumlunar.space 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 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. // 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{} hosts := []KnownHost{}
scanner := bufio.NewScanner(r) scanner := bufio.NewScanner(r)
@ -41,7 +57,7 @@ func ParseKnownHosts(r io.Reader) ([]KnownHost, error) {
hostname := parts[0] hostname := parts[0]
algorithm := parts[1] algorithm := parts[1]
fingerprint := parts[2] fingerprint := parts[2]
notAfter, err := strconv.ParseInt(parts[3], 10, 0) expires, err := strconv.ParseInt(parts[3], 10, 0)
if err != nil { if err != nil {
return nil, ErrInvalidKnownHosts return nil, ErrInvalidKnownHosts
} }
@ -50,13 +66,18 @@ func ParseKnownHosts(r io.Reader) ([]KnownHost, error) {
Hostname: hostname, Hostname: hostname,
Algorithm: algorithm, Algorithm: algorithm,
Fingerprint: fingerprint, Fingerprint: fingerprint,
NotAfter: notAfter, Expires: expires,
}) })
} }
return hosts, nil 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. // Fingerprint returns the SHA-512 fingerprint of the provided certificate.
func Fingerprint(cert *x509.Certificate) string { func Fingerprint(cert *x509.Certificate) string {
sum512 := sha512.Sum512(cert.Raw) sum512 := sha512.Sum512(cert.Raw)