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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
|
package core
import (
"bufio"
"bytes"
"fmt"
"os"
"strings"
"aead.dev/minisign"
"github.com/axtloss/fsverify/verify/config"
"github.com/tarm/serial"
)
// fileReadKey reads the public minisign key from a file specified in config.KeyLocation.
func fileReadKey() (string, error) {
if _, err := os.Stat(config.KeyLocation); os.IsNotExist(err) {
return "", fmt.Errorf("Key location %s does not exist", config.KeyLocation)
}
file, err := os.Open(config.KeyLocation)
if err != nil {
return "", err
}
defer file.Close()
// A public key is never longer than 56 bytes
key := make([]byte, 56)
reader := bufio.NewReader(file)
n, err := reader.Read(key)
if n != 56 {
return "", fmt.Errorf("Key does not match expected key size. Expected 56, got %d", n)
}
if err != nil {
return "", err
}
return string(key), nil
}
// serialReadKey reads the public minisign key from a usb tty specified in config.KeyLocation.
func serialReadKey() (string, error) {
// Since the usb serial is tested with an arduino
// it is assumed that the tty device does not always exist
// and can be manually plugged in by the user
if _, err := os.Stat(config.KeyLocation); !os.IsNotExist(err) {
fmt.Println("Reconnect arduino now")
for true {
if _, err := os.Stat(config.KeyLocation); os.IsNotExist(err) {
break
}
}
} else {
fmt.Println("Connect arduino now")
}
for true {
if _, err := os.Stat(config.KeyLocation); !os.IsNotExist(err) {
break
}
}
fmt.Println("Arduino connected")
c := &serial.Config{Name: config.KeyLocation, Baud: 9600}
s, err := serial.OpenPort(c)
if err != nil {
return "", err
}
key := ""
for true {
buf := make([]byte, 128)
n, err := s.Read(buf)
if err != nil {
return "", err
}
defer s.Close()
key = key + fmt.Sprintf("%q", buf[:n])
// ensure that two tab sequences are read
// meaning that the entire key has been captured
// since the key is surrounded by a tab sequence
if strings.Count(key, "\\t") == 2 {
break
}
}
key = strings.ReplaceAll(key, "\\t", "")
key = strings.ReplaceAll(key, "\"", "")
if len(key) != 56 {
return "", fmt.Errorf("Key does not match expected key size. Expected 56, got %d", len(key))
}
return key, nil
}
// ReadKey is a wrapper function to call the proper readKey function according to config.KeyStore.
func ReadKey() (string, error) {
switch config.KeyStore {
case 0:
return fileReadKey()
case 1:
return fileReadKey()
case 2:
return "", nil // TPM
case 3:
return serialReadKey()
}
return "", nil
}
// ReadBlock reads a data area of a bytes.Reader specified in the given node.
// It additionally verifies that the amount of bytes read equal the wanted amount and returns an error if this is not the case.
func ReadBlock(node Node, part *bytes.Reader, totalReadBlocks int) ([]byte, int, error) {
if node.BlockEnd-node.BlockStart < 0 {
return []byte{}, -1, fmt.Errorf("tried creating byte slice with negative length. %d to %d total %d\n", node.BlockStart, node.BlockEnd, node.BlockEnd-node.BlockStart)
}
block := make([]byte, node.BlockEnd-node.BlockStart)
blockSize := node.BlockEnd - node.BlockStart
_, err := part.Seek(int64(node.BlockStart), 0)
if err != nil {
return []byte{}, -1, err
}
n, err := part.Read(block)
if err != nil {
return block, -1, err
} else if n != blockSize {
return block, -1, fmt.Errorf("Did not read correct amount of bytes. Expected: %d, Got: %d", blockSize, n)
}
return block, totalReadBlocks + 1, err
}
// VerifySignature verifies the database using a given signature and public key.
func VerifySignature(key string, signature string, database string) (bool, error) {
var pk minisign.PublicKey
if err := pk.UnmarshalText([]byte(key)); err != nil {
return false, err
}
data, err := os.ReadFile(database)
if err != nil {
return false, err
}
return minisign.Verify(pk, data, []byte(signature)), nil
}
// VerifyBlock verifies a byte slice with the hash in a given Node.
func VerifyBlock(block []byte, node Node) error {
calculatedBlockHash, err := CalculateBlockHash(block)
if err != nil {
return err
}
wantedBlockHash := node.BlockSum
if strings.Compare(calculatedBlockHash, strings.TrimSpace(wantedBlockHash)) == 0 {
return nil
}
return fmt.Errorf("Node %s ranging from %d to %d does not match block. Expected %s, got %s.", node.PrevNodeSum, node.BlockStart, node.BlockEnd, wantedBlockHash, calculatedBlockHash)
}
// VerifyNode verifies that the current Node is valid by matching the checksum of it with the PrevNodeSum field of the next node.
func VerifyNode(node Node, nextNode Node) error {
nodeHash, err := calculateStringHash(fmt.Sprintf("%d%d%s%s", node.BlockStart, node.BlockEnd, node.BlockSum, node.PrevNodeSum))
if err != nil {
return err
}
if strings.Compare(nodeHash, nextNode.PrevNodeSum) != 0 {
return fmt.Errorf("Node %s is not valid!", node.PrevNodeSum)
}
return nil
}
|