aboutsummaryrefslogtreecommitdiff
path: root/pkg/types/leaf.go
blob: 99cf08abf4a3ed57174417bea2c3d9ddf690e4e1 (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
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
package types

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

	"git.sigsum.org/sigsum-go/pkg/ascii"
	"git.sigsum.org/sigsum-go/pkg/merkle"
)

type Statement struct {
	ShardHint uint64      `ascii:"shard_hint"`
	Checksum  merkle.Hash `ascii:"checksum"`
}

type Leaf struct {
	Statement
	Signature Signature   `ascii:"signature"`
	KeyHash   merkle.Hash `ascii:"key_hash"`
}

type Leaves []Leaf

func (s *Statement) ToBinary() []byte {
	namespace := fmt.Sprintf("tree_leaf:v0:%d@sigsum.org", s.ShardHint)
	b := make([]byte, 6+4+len(namespace)+4+0+4+6+4+merkle.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(s.Checksum[:]))

	return b
}

func (s *Statement) Sign(signer crypto.Signer) (*Signature, error) {
	sig, err := signer.Sign(nil, s.ToBinary(), crypto.Hash(0))
	if err != nil {
		return nil, fmt.Errorf("types: failed signing statement")
	}

	var signature Signature
	copy(signature[:], sig)
	return &signature, nil
}

func (s *Statement) Verify(key *PublicKey, sig *Signature) bool {
	return ed25519.Verify(ed25519.PublicKey(key[:]), s.ToBinary(), sig[:])
}

func (l *Leaf) ToBinary() []byte {
	b := make([]byte, 136)
	binary.BigEndian.PutUint64(b[0:8], l.ShardHint)
	copy(b[8:40], l.Checksum[:])
	copy(b[40:104], l.Signature[:])
	copy(b[104:136], l.KeyHash[:])
	return b
}

func (l *Leaf) FromBinary(b []byte) error {
	if len(b) != 136 {
		return fmt.Errorf("types: invalid leaf size: %d", len(b))
	}

	l.ShardHint = binary.BigEndian.Uint64(b[0:8])
	copy(l.Checksum[:], b[8:40])
	copy(l.Signature[:], b[40:104])
	copy(l.KeyHash[:], b[104:136])
	return nil
}

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

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

func (l *Leaves) FromASCII(r io.Reader) error {
	leaves := &struct {
		ShardHint []uint64      `ascii:"shard_hint"`
		Checksum  []merkle.Hash `ascii:"checksum"`
		Signature []Signature   `ascii:"signature"`
		KeyHash   []merkle.Hash `ascii:"key_hash"`
	}{}

	if err := ascii.StdEncoding.Deserialize(r, leaves); err != nil {
		return err
	}
	n := len(leaves.ShardHint)
	if n != len(leaves.Checksum) {
		return fmt.Errorf("types: mismatched leaf field counts")
	}
	if n != len(leaves.Signature) {
		return fmt.Errorf("types: mismatched leaf field counts")
	}
	if n != len(leaves.KeyHash) {
		return fmt.Errorf("types: mismatched leaf field counts")
	}

	*l = make([]Leaf, 0, n)
	for i := 0; i < n; i++ {
		*l = append(*l, Leaf{
			Statement: Statement{
				ShardHint: leaves.ShardHint[i],
				Checksum:  leaves.Checksum[i],
			},
			Signature: leaves.Signature[i],
			KeyHash:   leaves.KeyHash[i],
		})
	}
	return nil
}