From e18d36ebae30536c77c61cd5da123991e0ca1629 Mon Sep 17 00:00:00 2001 From: Rasmus Dahlberg Date: Sun, 31 Dec 2023 09:39:25 +0100 Subject: Add drafty prototype --- pkg/submission/submission.go | 104 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 pkg/submission/submission.go (limited to 'pkg/submission/submission.go') diff --git a/pkg/submission/submission.go b/pkg/submission/submission.go new file mode 100644 index 0000000..d33a49d --- /dev/null +++ b/pkg/submission/submission.go @@ -0,0 +1,104 @@ +// 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/silent-ct/pkg/policy" +) + +const ( + Separator = "silent-ct: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() +} -- cgit v1.2.3