forked from onflow/flow-go
-
Notifications
You must be signed in to change notification settings - Fork 0
/
utils.go
278 lines (222 loc) · 7.63 KB
/
utils.go
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
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
package cmd
import (
"crypto/rand"
"crypto/sha256"
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
"strings"
"golang.org/x/crypto/nacl/box"
"github.com/onflow/flow-go/cmd/build"
"github.com/onflow/flow-go/model/bootstrap"
"github.com/onflow/flow-go/model/flow"
ioutils "github.com/onflow/flow-go/utils/io"
)
const fileMode = os.FileMode(0644)
var (
FilenameTransitKeyPub = "transit-key.pub.%v"
FilenameTransitKeyPriv = "transit-key.priv.%v"
FilenameRandomBeaconCipher = bootstrap.FilenameRandomBeaconPriv + ".%v.enc"
// default files to upload for all role type
filesToUpload = []string{
bootstrap.PathNodeInfoPub,
}
// consensus node additionally will need the transit key (to securely transport DKG in phase 2)
filesToUploadConsensus = FilenameTransitKeyPub
// default folder to download for all role type
folderToDownload = bootstrap.DirnamePublicBootstrap
// consensus node additionally gets the random beacon file
filesToDownloadConsensus = FilenameRandomBeaconCipher
// commit and semver vars
commit = build.Commit()
semver = build.Semver()
)
// readNodeID reads the NodeID file
func readNodeID() (string, error) {
path := filepath.Join(flagBootDir, bootstrap.PathNodeID)
data, err := ioutils.ReadFile(path)
if err != nil {
return "", fmt.Errorf("error reading file %s: %w", path, err)
}
return strings.TrimSpace(string(data)), nil
}
func getAdditionalFilesToDownload(role flow.Role, nodeID string) []string {
switch role {
case flow.RoleConsensus:
return []string{fmt.Sprintf(filesToDownloadConsensus, nodeID)}
}
return make([]string, 0)
}
func getFilesToUpload(role flow.Role) []string {
switch role {
case flow.RoleConsensus:
return append(filesToUpload, filesToUploadConsensus)
default:
return filesToUpload
}
}
func getFileSHA256(file string) (string, error) {
f, err := os.Open(file)
if err != nil {
return "", err
}
defer f.Close()
h := sha256.New()
if _, err := io.Copy(h, f); err != nil {
return "", err
}
return fmt.Sprintf("%x", h.Sum(nil)), nil
}
// moveFile moves a file from source to destination where src and dst are full paths including the filename
func moveFile(src, dst string) error {
// check if source file exist
if !ioutils.FileExists(src) {
return fmt.Errorf("file not found: %s", src)
}
// create the destination dir if it does not exist
destinationDir := filepath.Dir(dst)
err := os.MkdirAll(destinationDir, 0755)
if err != nil {
return fmt.Errorf("failed to create directory %s: %w", destinationDir, err)
}
// first, try renaming the file
err = os.Rename(src, dst)
if err == nil {
// if renaming works, we are done
return nil
}
// renaming may fail if the destination dir is on a different disk, in that case we do a copy followed by remove
// open the source file
source, err := os.Open(src)
if err != nil {
return fmt.Errorf("failed to open file %s: %w", src, err)
}
// create the destination file
destination, err := os.Create(dst)
if err != nil {
return fmt.Errorf("failed to create file %s: %w", dst, err)
}
defer destination.Close()
// copy the source file to the destination file
_, err = io.Copy(destination, source)
if err != nil {
errorStr := err.Error()
closeErr := source.Close()
if closeErr != nil {
errorStr = fmt.Sprintf("%s, %s", errorStr, closeErr)
}
return fmt.Errorf("failed to copy file %s to %s: %s", src, dst, errorStr)
}
// close the source file
err = source.Close()
if err != nil {
return fmt.Errorf("failed to close source file %s: %w", src, err)
}
// flush the destination file
err = destination.Sync()
if err != nil {
return fmt.Errorf("failed to copy file %s to %s: %w", src, dst, err)
}
// read the source file permissions
si, err := os.Stat(src)
if err != nil {
return fmt.Errorf("failed to get file information %s: %w", src, err)
}
// set the same permissions on the destination file
err = os.Chmod(dst, si.Mode())
if err != nil {
return fmt.Errorf("failed to set permisson on file %s: %w", dst, err)
}
// delete the source file
err = os.Remove(src)
if err != nil {
return fmt.Errorf("failed removing original file: %s", err)
}
return nil
}
func unWrapFile(bootDir string, nodeID string) error {
log.Info().Msg("decrypting Random Beacon key")
pubKeyPath := filepath.Join(bootDir, fmt.Sprintf(FilenameTransitKeyPub, nodeID))
privKeyPath := filepath.Join(bootDir, fmt.Sprintf(FilenameTransitKeyPriv, nodeID))
ciphertextPath := filepath.Join(bootDir, fmt.Sprintf(FilenameRandomBeaconCipher, nodeID))
plaintextPath := filepath.Join(bootDir, fmt.Sprintf(bootstrap.PathRandomBeaconPriv, nodeID))
ciphertext, err := ioutils.ReadFile(ciphertextPath)
if err != nil {
return fmt.Errorf("failed to open ciphertext file %s: %w", ciphertextPath, err)
}
publicKey, err := ioutils.ReadFile(pubKeyPath)
if err != nil {
return fmt.Errorf("failed to open public keyfile %s: %w", pubKeyPath, err)
}
privateKey, err := ioutils.ReadFile(privKeyPath)
if err != nil {
return fmt.Errorf("failed to open private keyfile %s: %w", privKeyPath, err)
}
// NaCl is picky and wants its type to be exactly a [32]byte, but readfile reads a slice
var pubKeyBytes, privKeyBytes [32]byte
copy(pubKeyBytes[:], publicKey)
copy(privKeyBytes[:], privateKey)
plaintext := make([]byte, 0, len(ciphertext)-box.AnonymousOverhead)
plaintext, ok := box.OpenAnonymous(plaintext, ciphertext, &pubKeyBytes, &privKeyBytes)
if !ok {
return fmt.Errorf("failed to decrypt random beacon key using private key from file: %s", privKeyPath)
}
err = ioutil.WriteFile(plaintextPath, plaintext, fileMode)
if err != nil {
return fmt.Errorf("failed to write the decrypted file %s: %w", plaintextPath, err)
}
return nil
}
func wrapFile(bootDir string, nodeID string) error {
pubKeyPath := filepath.Join(bootDir, fmt.Sprintf(FilenameTransitKeyPub, nodeID))
plaintextPath := filepath.Join(bootDir, fmt.Sprintf(bootstrap.PathRandomBeaconPriv, nodeID))
ciphertextPath := filepath.Join(bootDir, fmt.Sprintf(FilenameRandomBeaconCipher, nodeID))
plaintext, err := ioutils.ReadFile(plaintextPath)
if err != nil {
return fmt.Errorf("failed to open plaintext file %s: %w", plaintextPath, err)
}
publicKey, err := ioutils.ReadFile(pubKeyPath)
if err != nil {
return fmt.Errorf("faield to open public keyfile %s: %w", pubKeyPath, err)
}
var pubKeyBytes [32]byte
copy(pubKeyBytes[:], publicKey)
ciphertext := make([]byte, 0, len(plaintext)+box.AnonymousOverhead)
ciphertext, err = box.SealAnonymous(ciphertext, plaintext, &pubKeyBytes, rand.Reader)
if err != nil {
return fmt.Errorf("could not encrypt file: %w", err)
}
err = ioutil.WriteFile(ciphertextPath, ciphertext, fileMode)
if err != nil {
return fmt.Errorf("error writing ciphertext: %w", err)
}
return nil
}
// generateKeys creates the transit keypair and writes them to disk for later
func generateKeys(bootDir string, nodeID string) error {
privPath := filepath.Join(bootDir, fmt.Sprintf(FilenameTransitKeyPriv, nodeID))
pubPath := filepath.Join(bootDir, fmt.Sprintf(FilenameTransitKeyPub, nodeID))
if ioutils.FileExists(privPath) && ioutils.FileExists(pubPath) {
log.Warn().Msg("transit-key-path priv & pub both exist, skipping key generation")
return nil
}
log.Info().Msg("generating key pair")
// Generate the keypair
pub, priv, err := box.GenerateKey(rand.Reader)
if err != nil {
return fmt.Errorf("failed to create keys: %w", err)
}
// Write private key file
err = ioutil.WriteFile(privPath, priv[:], fileMode)
if err != nil {
return fmt.Errorf("failed to write pivate key file: %w", err)
}
// Write public key file
err = ioutil.WriteFile(pubPath, pub[:], fileMode)
if err != nil {
return fmt.Errorf("failed to write public key file: %w", err)
}
return nil
}