aboutsummaryrefslogtreecommitdiff
path: root/pkg/submission/submission.go
diff options
context:
space:
mode:
authorRasmus Dahlberg <rasmus@rgdd.se>2023-12-31 09:39:25 +0100
committerRasmus Dahlberg <rasmus@rgdd.se>2024-01-07 20:22:23 +0100
commite18d36ebae30536c77c61cd5da123991e0ca1629 (patch)
treebf4880c0019a6009ab1b671e23ef4a1a4a5e8e08 /pkg/submission/submission.go
parent54d980afcbd6f0011d6a162e0003587d26a3e311 (diff)
Add drafty prototype
Diffstat (limited to 'pkg/submission/submission.go')
-rw-r--r--pkg/submission/submission.go104
1 files changed, 104 insertions, 0 deletions
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()
+}