package main import ( "bufio" "crypto/sha256" "crypto/x509" "encoding/hex" "errors" "os" "path/filepath" "strconv" "strings" "time" ) var hosts map[string]Host type Host struct { // string since why convert it to bytes and handle errors when you can just not Fingerprint string NotAfter time.Time } func populateHosts() error { file, err := os.OpenFile(filepath.Join(xdgDataHome(), "konbata", "known_hosts_2"), os.O_RDONLY|os.O_CREATE, 0600) if err != nil { return err } defer file.Close() scanner := bufio.NewScanner(file) hosts = make(map[string]Host) for scanner.Scan() { if scanner.Text() == "" { continue } values := strings.Split(scanner.Text(), " ") if len(values) != 3 { return errors.New("malformed host line encountered") } hostname := values[0] fingerprint := values[1] timeInt, err := strconv.ParseInt(values[2], 10, 0) if err != nil { return err } hosts[hostname] = Host{ Fingerprint: fingerprint, NotAfter: time.UnixMicro(timeInt), } } return nil } func saveHostsTmp(filename string) error { file, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE, 0600) if err != nil { return err } defer file.Close() err = file.Truncate(0) if err != nil { return err } writer := bufio.NewWriter(file) for hostname, host := range hosts { _, err = writer.WriteString(hostname) if err != nil { return err } _, err = writer.WriteRune(' ') if err != nil { return err } _, err = writer.WriteString(host.Fingerprint) if err != nil { return err } _, err = writer.WriteRune(' ') if err != nil { return err } _, err = writer.WriteString(strconv.FormatInt(host.NotAfter.UnixMicro(), 10)) if err != nil { return err } _, err = writer.WriteRune('\n') if err != nil { return err } } return writer.Flush() } func saveHosts() error { filename := filepath.Join(xdgDataHome(), "konbata", "known_hosts_2") tmpFilename := filepath.Join(xdgDataHome(), "konbata", "known_hosts_2.tmp") err := saveHostsTmp(tmpFilename) if err != nil { return err } return os.Rename(tmpFilename, filename) } func TrustCertificate(hostname string, cert *x509.Certificate) error { hash := sha256.New() hash.Write(cert.Raw) fingerprint := hex.EncodeToString(hash.Sum(nil)) currentTime := time.Now() host, ok := hosts[hostname] if !ok || currentTime.After(host.NotAfter) { if currentTime.Before(cert.NotBefore) { return errors.New("cert is used before its not before value") } hosts[hostname] = Host{ Fingerprint: fingerprint, NotAfter: cert.NotAfter, } return saveHosts() } else if host.Fingerprint != fingerprint { return errors.New("fingerprint does not match!") } return nil }