tofu: Use base64-encoded sha256 fingerprints

This commit is contained in:
Adnan Maolood 2021-03-06 15:24:14 -05:00
parent c639233ea1
commit 6e5c2473e7
2 changed files with 18 additions and 67 deletions

View File

@ -6,7 +6,6 @@ package main
import ( import (
"bufio" "bufio"
"bytes"
"context" "context"
"crypto/x509" "crypto/x509"
"errors" "errors"
@ -64,7 +63,7 @@ func trustCertificate(hostname string, cert *x509.Certificate) error {
knownHost, ok := hosts.Lookup(hostname) knownHost, ok := hosts.Lookup(hostname)
if ok { if ok {
// Check fingerprint // Check fingerprint
if bytes.Equal(knownHost.Fingerprint, host.Fingerprint) { if knownHost.Fingerprint != host.Fingerprint {
return nil return nil
} }
return errors.New("error: fingerprint does not match!") return errors.New("error: fingerprint does not match!")

View File

@ -4,14 +4,14 @@ package tofu
import ( import (
"bufio" "bufio"
"bytes" "bytes"
"crypto/sha512" "crypto/sha256"
"crypto/x509" "crypto/x509"
"encoding/base64"
"errors" "errors"
"fmt" "fmt"
"io" "io"
"os" "os"
"sort" "sort"
"strconv"
"strings" "strings"
"sync" "sync"
) )
@ -150,7 +150,7 @@ func (k *KnownHosts) TOFU(hostname string, cert *x509.Certificate) error {
k.Add(host) k.Add(host)
return nil return nil
} }
if !bytes.Equal(knownHost.Fingerprint, host.Fingerprint) { if host.Fingerprint != knownHost.Fingerprint {
return fmt.Errorf("fingerprint for %q does not match", hostname) return fmt.Errorf("fingerprint for %q does not match", hostname)
} }
return nil return nil
@ -267,7 +267,7 @@ func (p *PersistentHosts) TOFU(hostname string, cert *x509.Certificate) error {
if !ok { if !ok {
return p.Add(host) return p.Add(host)
} }
if !bytes.Equal(knownHost.Fingerprint, host.Fingerprint) { if host.Fingerprint != knownHost.Fingerprint {
return fmt.Errorf("fingerprint for %q does not match", hostname) return fmt.Errorf("fingerprint for %q does not match", hostname)
} }
return nil return nil
@ -280,20 +280,20 @@ func (p *PersistentHosts) Close() error {
// Host represents a host entry with a fingerprint using a certain algorithm. // Host represents a host entry with a fingerprint using a certain algorithm.
type Host struct { type Host struct {
Hostname string // hostname Hostname string // hostname
Algorithm string // fingerprint algorithm e.g. SHA-512 Algorithm string // fingerprint algorithm e.g. sha256
Fingerprint Fingerprint // fingerprint Fingerprint string // fingerprint
} }
// NewHost returns a new host with a SHA-512 fingerprint of // NewHost returns a new host with a SHA256 fingerprint of
// the provided raw data. // the provided raw data.
func NewHost(hostname string, raw []byte) Host { func NewHost(hostname string, raw []byte) Host {
sum := sha512.Sum512(raw) sum := sha256.Sum256(raw)
return Host{ return Host{
Hostname: hostname, Hostname: hostname,
Algorithm: "SHA-512", Algorithm: "sha256",
Fingerprint: sum[:], Fingerprint: base64.StdEncoding.EncodeToString(sum[:]),
} }
} }
@ -311,7 +311,7 @@ func (h Host) String() string {
b.WriteByte(' ') b.WriteByte(' ')
b.WriteString(h.Algorithm) b.WriteString(h.Algorithm)
b.WriteByte(' ') b.WriteByte(' ')
b.WriteString(h.Fingerprint.String()) b.WriteString(h.Fingerprint)
return b.String() return b.String()
} }
@ -331,66 +331,18 @@ func (h *Host) UnmarshalText(text []byte) error {
h.Hostname = string(parts[0]) h.Hostname = string(parts[0])
algorithm := string(parts[1]) algorithm := string(parts[1])
if algorithm != "SHA-512" { if algorithm != "sha256" {
return fmt.Errorf("unsupported algorithm %q", algorithm) return fmt.Errorf("unsupported algorithm %q", algorithm)
} }
h.Algorithm = algorithm h.Algorithm = algorithm
fingerprint := make([]byte, 0, sha512.Size) fingerprint, err := base64.StdEncoding.DecodeString(string(parts[2]))
scanner := bufio.NewScanner(bytes.NewReader(parts[2])) if err != nil {
scanner.Split(scanFingerprint) return err
for scanner.Scan() {
b, err := strconv.ParseUint(scanner.Text(), 16, 8)
if err != nil {
return fmt.Errorf("failed to parse fingerprint hash: %w", err)
}
fingerprint = append(fingerprint, byte(b))
} }
if len(fingerprint) != sha512.Size { h.Fingerprint = string(fingerprint)
return fmt.Errorf("invalid fingerprint size %d, expected %d",
len(fingerprint), sha512.Size)
}
h.Fingerprint = fingerprint
return nil return nil
} }
func scanFingerprint(data []byte, atEOF bool) (advance int, token []byte, err error) {
if atEOF && len(data) == 0 {
return 0, nil, nil
}
if i := bytes.IndexByte(data, ':'); i >= 0 {
// We have a full newline-terminated line.
return i + 1, data[0:i], nil
}
// If we're at EOF, we have a final, non-terminated hex byte
if atEOF {
return len(data), data, nil
}
// Request more data.
return 0, nil, nil
}
// Fingerprint represents a fingerprint.
type Fingerprint []byte
// String returns a string representation of the fingerprint.
func (f Fingerprint) String() string {
var sb strings.Builder
for i, b := range f {
if i > 0 {
sb.WriteByte(':')
}
fmt.Fprintf(&sb, "%02X", b)
}
return sb.String()
}