aboutsummaryrefslogtreecommitdiff
path: root/pkg/types/tree_head.go
blob: 2ddeb0861f67193b764edca2cc8a63f8369273d7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
package types

import (
	"crypto"
	"crypto/ed25519"
	"encoding/binary"
	"fmt"
	"io"

	"git.sigsum.org/sigsum-lib-go/pkg/ascii"
	"git.sigsum.org/sigsum-lib-go/pkg/hex"
)

type TreeHead struct {
	Timestamp uint64 `ascii:"timestamp"`
	TreeSize  uint64 `ascii:"tree_size"`
	RootHash  Hash   `ascii:"root_hash"`
}

type SignedTreeHead struct {
	TreeHead
	Signature Signature `ascii:"signature"`
}

type CosignedTreeHead struct {
	SignedTreeHead
	Cosignature []Signature `ascii:"cosignature"`
	KeyHash     []Hash      `ascii:"key_hash"`
}

func (th *TreeHead) toBinary() []byte {
	b := make([]byte, 48)
	binary.BigEndian.PutUint64(b[0:8], th.Timestamp)
	binary.BigEndian.PutUint64(b[8:16], th.TreeSize)
	copy(b[16:48], th.RootHash[:])
	return b
}

func (th *TreeHead) ToBinary(keyHash *Hash) []byte {
	namespace := fmt.Sprintf("tree_head:v0:%s@sigsum.org", hex.Serialize(keyHash[:])) // length 88
	b := make([]byte, 6+4+88+4+0+4+6+4+HashSize)

	copy(b[0:6], "SSHSIG")
	i := 6
	i += putSSHString(b[i:], namespace)
	i += putSSHString(b[i:], "")
	i += putSSHString(b[i:], "sha256")
	i += putSSHString(b[i:], string((*HashFn(th.toBinary()))[:]))

	return b
}

func (th *TreeHead) Sign(s crypto.Signer, kh *Hash) (*SignedTreeHead, error) {
	sig, err := s.Sign(nil, th.ToBinary(kh), crypto.Hash(0))
	if err != nil {
		return nil, fmt.Errorf("types: failed signing tree head")
	}

	sth := &SignedTreeHead{
		TreeHead: *th,
	}
	copy(sth.Signature[:], sig)
	return sth, nil
}

func (sth *SignedTreeHead) ToASCII(w io.Writer) error {
	return ascii.StdEncoding.Serialize(w, sth)
}

func (sth *SignedTreeHead) FromASCII(r io.Reader) error {
	return ascii.StdEncoding.Deserialize(r, sth)
}

func (sth *SignedTreeHead) Verify(key *PublicKey, kh *Hash) bool {
	return ed25519.Verify(ed25519.PublicKey(key[:]), sth.TreeHead.ToBinary(kh), sth.Signature[:])
}

func (cth *CosignedTreeHead) ToASCII(w io.Writer) error {
	return ascii.StdEncoding.Serialize(w, cth)
}

func (cth *CosignedTreeHead) FromASCII(r io.Reader) error {
	if err := ascii.StdEncoding.Deserialize(r, cth); err != nil {
		return err
	}
	if len(cth.Cosignature) != len(cth.KeyHash) {
		return fmt.Errorf("types: mismatched cosignature count")
	}
	return nil
}