// Package submission provides creation and opening of a node's legitimately // issued certificate chains. A submission is composed of a name, a message // authentication code, and one or more certificate chains in PEM format. // // Note: this package makes no attempt to parse any certificate chain. In other // words, each certificate chain is treated as an opaque list of bytes. package submission import ( "bytes" "crypto/hmac" "crypto/sha256" "encoding/hex" "fmt" "strings" "rgdd.se/silentct/pkg/policy" ) const ( Separator = "silentct:separator\n" ) type Submission []byte func New(node policy.Node, data [][]byte) (Submission, error) { mac, err := node.HMAC(message(data)) if err != nil { return nil, fmt.Errorf("hmac: %v", err) } buf := bytes.NewBuffer(nil) buf.WriteString(fmt.Sprintf("%s %x\n", node.Name, mac[:])) buf.Write(message(data)) return buf.Bytes(), nil } func (s *Submission) Peek() (name string, err error) { name, _, _, err = s.split() return } func (s *Submission) Open(node policy.Node) ([][]byte, error) { name, gotMAC, msg, err := s.split() if err != nil { return nil, err } if name != node.Name { return nil, fmt.Errorf("wrong node name %s", name) } wantMAC, err := node.HMAC(msg) if err != nil { return nil, fmt.Errorf("hmac: %v", err) } if !hmac.Equal(gotMAC[:], wantMAC[:]) { return nil, fmt.Errorf("hmac: mismatch") } return bytes.Split(msg, []byte(Separator)), nil } func (s *Submission) split() (name string, mac [sha256.Size]byte, msg []byte, err error) { i := bytes.IndexByte(*s, '\n') if i == -1 { return name, mac, msg, fmt.Errorf("no authorization line") } authLine := (*s)[:i] msg = (*s)[i+1:] split := strings.Split(string(authLine), " ") if len(split) != 2 { return name, mac, msg, fmt.Errorf("invalid authorization line") } b, err := hex.DecodeString(split[1]) if err != nil { return name, mac, msg, fmt.Errorf("mac: %v", err) } if len(b) != len(mac) { return name, mac, msg, fmt.Errorf("mac: length must be %d bytes", len(mac)) } name = split[0] copy(mac[:], b) return } func message(data [][]byte) []byte { if len(data) == 0 { return []byte{} } buf := bytes.NewBuffer(data[0]) for i, d := range data[1:] { prev := data[i] if prev[len(prev)-1] != '\n' { buf.Write([]byte("\n")) } buf.Write([]byte(Separator)) buf.Write(d) } return buf.Bytes() }