package policy import ( "crypto/hmac" "crypto/sha256" "encoding/json" "fmt" "io" "golang.org/x/crypto/hkdf" ) type Node struct { Name string `json:"name"` // Artbirary node name to authenticate Secret string `json:"secret"` // Arbitrary node secret for authentication URL string `json:"url"` // Where the node's submissions can be downloaded Domains []string `json:"issues"` // Exact-match domain names allowed to be issued key [16]byte } func NewNode(name, secret, url string, domains []string) (Node, error) { n := Node{Name: name, Secret: secret, Domains: domains, URL: url} if err := n.deriveKey(); err != nil { return Node{}, err } return n, n.Validate() } func (n *Node) UnmarshalJSON(data []byte) error { type internal Node if err := json.Unmarshal(data, (*internal)(n)); err != nil { return err } if err := n.deriveKey(); err != nil { return err } return n.Validate() } func (n *Node) Validate() error { if n.Name == "" { return fmt.Errorf("name is required") } if n.Secret == "" { return fmt.Errorf("secret is required") } if n.URL == "" { return fmt.Errorf("url is required") } if n.key == [16]byte{} { return fmt.Errorf("key needs to be derived") } return nil } func (n *Node) Authorize(sans []string) error { for _, san := range sans { ok := false for _, domain := range n.Domains { if domain == san { ok = true break } } if !ok { return fmt.Errorf("node %s is not authorized to issue certificate with name %s", n.Name, san) } } return nil } func (n *Node) HMAC(data []byte) (mac [sha256.Size]byte, err error) { if err = n.Validate(); err != nil { return } h := hmac.New(sha256.New, n.key[:]) _, err = h.Write(data) copy(mac[:], h.Sum(nil)) return } func (n *Node) deriveKey() error { const salt = "silent-ct" hkdf := hkdf.New(sha256.New, []byte(n.Secret), []byte(salt), []byte(n.Name)) _, err := io.ReadFull(hkdf, n.key[:]) return err }