mirror of
https://git.sr.ht/~adnano/go-gemini
synced 2024-11-26 22:18:24 +01:00
Differentiate between unknown and untrusted certificates
This commit is contained in:
parent
1196c4013b
commit
6a5a7c40c5
30
README.md
30
README.md
@ -51,9 +51,9 @@ clients. Here is a simple client using TOFU to authenticate certificates:
|
||||
```go
|
||||
client := &gemini.Client{
|
||||
KnownHosts: gemini.LoadKnownHosts(".local/share/gemini/known_hosts"),
|
||||
TrustCertificate: func(cert *x509.Certificate, knownHosts *gemini.KnownHosts) bool {
|
||||
TrustCertificate: func(cert *x509.Certificate, knownHosts *gemini.KnownHosts) error {
|
||||
// If the certificate is in the known hosts list, allow the connection
|
||||
if knownHosts.Has(cert) {
|
||||
if err := knownHosts.Lookup(cert); {
|
||||
return true
|
||||
}
|
||||
// Prompt the user
|
||||
@ -70,3 +70,29 @@ client := &gemini.Client{
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
```go
|
||||
client := &gemini.Client{
|
||||
TrustCertificate: func(cert *x509.Certificate, knownHosts *gemini.KnownHosts) error {
|
||||
err := knownHosts.Lookup(cert)
|
||||
if err != nil {
|
||||
switch err {
|
||||
case gemini.ErrCertificateNotTrusted:
|
||||
// Alert the user that the certificate is not trusted
|
||||
alertUser()
|
||||
case gemini.ErrCertificateUnknown:
|
||||
// Prompt the user to trust the certificate
|
||||
if userTrustsCertificateTemporarily() {
|
||||
// Temporarily trust the certificate
|
||||
return nil
|
||||
} else if user.TrustsCertificatePermanently() {
|
||||
// Add the certificate to the known hosts file
|
||||
knownHosts.Add(cert)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return err
|
||||
},
|
||||
}
|
||||
```
|
||||
|
@ -19,6 +19,7 @@ var (
|
||||
ErrInvalidURL = errors.New("gemini: requested URL is invalid")
|
||||
ErrCertificateNotValid = errors.New("gemini: certificate is invalid")
|
||||
ErrCertificateNotTrusted = errors.New("gemini: certificate is not trusted")
|
||||
ErrCertificateUnknown = errors.New("gemini: certificate is unknown")
|
||||
)
|
||||
|
||||
// Request represents a Gemini request.
|
||||
@ -171,7 +172,8 @@ type Client struct {
|
||||
|
||||
// TrustCertificate, if not nil, will be called to determine whether the
|
||||
// client should trust the given certificate.
|
||||
TrustCertificate func(cert *x509.Certificate, knownHosts *KnownHosts) bool
|
||||
// If error is not nil, the connection will be aborted.
|
||||
TrustCertificate func(cert *x509.Certificate, knownHosts *KnownHosts) error
|
||||
}
|
||||
|
||||
// Send sends a Gemini request and returns a Gemini response.
|
||||
@ -196,8 +198,8 @@ func (c *Client) Send(req *Request) (*Response, error) {
|
||||
if c.KnownHosts == nil || !c.KnownHosts.Has(cert) {
|
||||
return ErrCertificateNotTrusted
|
||||
}
|
||||
} else if !c.TrustCertificate(cert, c.KnownHosts) {
|
||||
return ErrCertificateNotTrusted
|
||||
} else if err := c.TrustCertificate(cert, c.KnownHosts); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
},
|
||||
|
@ -15,9 +15,9 @@ import (
|
||||
|
||||
var (
|
||||
client = &gemini.Client{
|
||||
TrustCertificate: func(cert *x509.Certificate, knownHosts *gemini.KnownHosts) bool {
|
||||
TrustCertificate: func(cert *x509.Certificate, knownHosts *gemini.KnownHosts) error {
|
||||
// Trust all certificates
|
||||
return true
|
||||
return nil
|
||||
},
|
||||
}
|
||||
cert tls.Certificate
|
||||
|
27
tofu.go
27
tofu.go
@ -71,6 +71,33 @@ func (k *KnownHosts) Has(cert *x509.Certificate) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// Lookup looks for the provided certificate in the list of known hosts.
|
||||
// If the hostname is in the list, but the fingerprint differs,
|
||||
// Lookup returns ErrCertificateNotTrusted.
|
||||
// If the hostname is not in the list, Lookup returns ErrCertificateUnknown.
|
||||
// If the certificate is found and the fingerprint matches, error will be nil.
|
||||
func (k *KnownHosts) Lookup(cert *x509.Certificate) error {
|
||||
now := time.Now().Unix()
|
||||
hostname := cert.Subject.CommonName
|
||||
fingerprint := Fingerprint(cert)
|
||||
for i := range k.hosts {
|
||||
if k.hosts[i].Hostname != hostname {
|
||||
continue
|
||||
}
|
||||
if k.hosts[i].Expires <= now {
|
||||
// Certificate is expired
|
||||
continue
|
||||
}
|
||||
if k.hosts[i].Fingerprint == fingerprint {
|
||||
// Fingerprint matches
|
||||
return nil
|
||||
}
|
||||
// Fingerprint does not match
|
||||
return ErrCertificateNotTrusted
|
||||
}
|
||||
return ErrCertificateUnknown
|
||||
}
|
||||
|
||||
// Parse parses the provided reader and adds the parsed known hosts to the list.
|
||||
// Invalid lines are ignored.
|
||||
func (k *KnownHosts) Parse(r io.Reader) {
|
||||
|
Loading…
Reference in New Issue
Block a user