github.com/koko1123/flow-go-1@v0.29.6/cmd/bootstrap/utils/key_generation.go (about) 1 package utils 2 3 import ( 4 "crypto/rand" 5 "crypto/sha256" 6 "fmt" 7 gohash "hash" 8 "io" 9 10 sdk "github.com/onflow/flow-go-sdk" 11 12 "github.com/koko1123/flow-go-1/model/encodable" 13 14 "golang.org/x/crypto/hkdf" 15 16 sdkcrypto "github.com/onflow/flow-go-sdk/crypto" 17 18 "github.com/koko1123/flow-go-1/model/bootstrap" 19 "github.com/koko1123/flow-go-1/model/flow" 20 "github.com/onflow/flow-go/crypto" 21 ) 22 23 // these constants are defined in X9.62 section 4.2 and 4.3 24 // see https://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.202.2977&rep=rep1&type=pdf 25 // they indicate if the conversion to/from a public key (point) in compressed form must involve an inversion of the ordinate coordinate 26 const X962_NO_INVERSION = uint8(0x02) 27 const X962_INVERSION = uint8(0x03) 28 29 func GenerateMachineAccountKey(seed []byte) (crypto.PrivateKey, error) { 30 keys, err := GenerateKeys(crypto.ECDSAP256, 1, [][]byte{seed}) 31 if err != nil { 32 return nil, err 33 } 34 return keys[0], nil 35 } 36 37 // GenerateSecretsDBEncryptionKey generates an encryption key for encrypting a 38 // Badger database. 39 func GenerateSecretsDBEncryptionKey() ([]byte, error) { 40 // 32-byte key to use AES-256 41 // https://pkg.go.dev/github.com/dgraph-io/badger/v3#Options.WithEncryptionKey 42 const keyLen = 32 43 44 key := make([]byte, keyLen) 45 _, err := rand.Read(key) 46 if err != nil { 47 return nil, fmt.Errorf("could not generate key: %w", err) 48 } 49 return key, nil 50 } 51 52 // The unstaked nodes have special networking keys, in two aspects: 53 // - they use crypto.ECDSASecp256k1 keys, not crypto.ECDSAP256 keys, 54 // - they use only positive keys (in the sense that the elliptic curve point of their public key is positive) 55 // 56 // Thanks to various properties of the cryptographic algorithm and libp2p, 57 // this affords us to not have to maintain a table of flow.NodeID -> NetworkPublicKey 58 // for those numerous and ephemeral nodes. 59 // It incurs a one-bit security reduction, which is deemed acceptable. 60 61 // drawUnstakedKey draws a single positive ECDSASecp256k1 key, and returns an error otherwise. 62 func drawUnstakedKey(seed []byte) (crypto.PrivateKey, error) { 63 key, err := crypto.GeneratePrivateKey(crypto.ECDSASecp256k1, seed) 64 if err != nil { 65 // this should not happen 66 return nil, err 67 } else if key.PublicKey().EncodeCompressed()[0] == X962_INVERSION { 68 // negative key -> unsuitable 69 return nil, fmt.Errorf("Unsuitable negative key") 70 } 71 return key, nil 72 } 73 74 // GeneratePublicNetworkingKey draws ECDSASecp256k1 keys until finding a suitable one. 75 // though this will return fast, this is not constant-time and will leak ~1 bit of information through its runtime 76 func GeneratePublicNetworkingKey(seed []byte) (key crypto.PrivateKey, err error) { 77 hkdf := hkdf.New(func() gohash.Hash { return sha256.New() }, seed, nil, []byte("public network")) 78 round_seed := make([]byte, len(seed)) 79 max_iterations := 20 // 1/(2^20) failure chance 80 for i := 0; i < max_iterations; i++ { 81 if _, err = io.ReadFull(hkdf, round_seed); err != nil { 82 // the hkdf Reader should not fail 83 panic(err) 84 } 85 if key, err = drawUnstakedKey(round_seed); err == nil { 86 return 87 } 88 } 89 return 90 } 91 92 func GenerateUnstakedNetworkingKeys(n int, seeds [][]byte) ([]crypto.PrivateKey, error) { 93 if n != len(seeds) { 94 return nil, fmt.Errorf("n needs to match the number of seeds (%v != %v)", n, len(seeds)) 95 } 96 97 keys := make([]crypto.PrivateKey, n) 98 99 var err error 100 for i, seed := range seeds { 101 if keys[i], err = GeneratePublicNetworkingKey(seed); err != nil { 102 return nil, err 103 } 104 } 105 106 return keys, nil 107 } 108 109 func GenerateNetworkingKey(seed []byte) (crypto.PrivateKey, error) { 110 keys, err := GenerateKeys(crypto.ECDSAP256, 1, [][]byte{seed}) 111 if err != nil { 112 return nil, err 113 } 114 return keys[0], nil 115 } 116 117 func GenerateNetworkingKeys(n int, seeds [][]byte) ([]crypto.PrivateKey, error) { 118 return GenerateKeys(crypto.ECDSAP256, n, seeds) 119 } 120 121 func GenerateStakingKey(seed []byte) (crypto.PrivateKey, error) { 122 keys, err := GenerateKeys(crypto.BLSBLS12381, 1, [][]byte{seed}) 123 if err != nil { 124 return nil, err 125 } 126 return keys[0], nil 127 } 128 129 func GenerateStakingKeys(n int, seeds [][]byte) ([]crypto.PrivateKey, error) { 130 return GenerateKeys(crypto.BLSBLS12381, n, seeds) 131 } 132 133 func GenerateKeys(algo crypto.SigningAlgorithm, n int, seeds [][]byte) ([]crypto.PrivateKey, error) { 134 if n != len(seeds) { 135 return nil, fmt.Errorf("n needs to match the number of seeds (%v != %v)", n, len(seeds)) 136 } 137 138 keys := make([]crypto.PrivateKey, n) 139 140 var err error 141 for i, seed := range seeds { 142 if keys[i], err = crypto.GeneratePrivateKey(algo, seed); err != nil { 143 return nil, err 144 } 145 } 146 147 return keys, nil 148 } 149 150 // WriteJSONFileFunc is a function which writes a file during bootstrapping. It 151 // accepts the path for the file (relative to the bootstrapping root directory) 152 // and the value to write. The function must marshal the value as JSON and write 153 // the result to the given path. 154 type WriteJSONFileFunc func(relativePath string, value interface{}) error 155 156 // WriteFileFunc is the same as WriteJSONFileFunc, but it writes the bytes directly 157 // rather than marshalling a structure to json. 158 type WriteFileFunc func(relativePath string, data []byte) error 159 160 // WriteMachineAccountFiles writes machine account key files for a set of nodeInfos. 161 // Assumes that machine accounts have been created using the default execution state 162 // bootstrapping. Further assumes that the order of nodeInfos is the same order that 163 // nodes were registered during execution state bootstrapping. 164 // 165 // Only applicable for transient test networks. 166 func WriteMachineAccountFiles(chainID flow.ChainID, nodeInfos []bootstrap.NodeInfo, write WriteJSONFileFunc) error { 167 168 // ensure the chain ID is for a transient chain, where it is possible to 169 // infer machine account addresses this way 170 if !chainID.Transient() { 171 return fmt.Errorf("cannot write default machine account files for non-transient network") 172 } 173 174 // write machine account key files for each node which has a machine account (LN/SN) 175 // 176 // for the machine account key, we keep track of the address index to map 177 // the Flow address of the machine account to the key. 178 addressIndex := uint64(4) 179 for _, nodeInfo := range nodeInfos { 180 181 // retrieve private representation of the node 182 private, err := nodeInfo.Private() 183 if err != nil { 184 return err 185 } 186 187 // We use the network key for the machine account. Normally it would be 188 // a separate key. 189 190 // Accounts are generated in a known order during bootstrapping, and 191 // account addresses are deterministic based on order for a given chain 192 // configuration. During the bootstrapping we create 4 Flow accounts besides 193 // the service account (index 0) so node accounts will start at index 5. 194 // 195 // All nodes have a staking account created for them, only collection and 196 // consensus nodes have a second machine account created. 197 // 198 // The accounts are created in the same order defined by the identity list 199 // provided to BootstrapProcedure, which is the same order as this iteration. 200 if nodeInfo.Role == flow.RoleCollection || nodeInfo.Role == flow.RoleConsensus { 201 // increment the address index to account for both the staking account 202 // and the machine account. 203 // now addressIndex points to the machine account address index 204 addressIndex += 2 205 } else { 206 // increment the address index to account for the staking account 207 // we don't need to persist anything related to the staking account 208 addressIndex += 1 209 continue 210 } 211 212 accountAddress, err := chainID.Chain().AddressAtIndex(addressIndex) 213 if err != nil { 214 return err 215 } 216 217 info := bootstrap.NodeMachineAccountInfo{ 218 Address: accountAddress.HexWithPrefix(), 219 EncodedPrivateKey: private.NetworkPrivKey.Encode(), 220 KeyIndex: 0, 221 SigningAlgorithm: private.NetworkPrivKey.Algorithm(), 222 HashAlgorithm: sdkcrypto.SHA3_256, 223 } 224 225 path := fmt.Sprintf(bootstrap.PathNodeMachineAccountInfoPriv, nodeInfo.NodeID) 226 err = write(path, info) 227 if err != nil { 228 return err 229 } 230 } 231 232 return nil 233 } 234 235 func WriteMachineAccountFile( 236 nodeID flow.Identifier, 237 accountAddress sdk.Address, 238 accountKey encodable.MachineAccountPrivKey, 239 write WriteJSONFileFunc) error { 240 241 info := bootstrap.NodeMachineAccountInfo{ 242 Address: fmt.Sprintf("0x%s", accountAddress.Hex()), 243 EncodedPrivateKey: accountKey.Encode(), 244 KeyIndex: 0, 245 SigningAlgorithm: accountKey.Algorithm(), 246 HashAlgorithm: sdkcrypto.SHA3_256, 247 } 248 249 path := fmt.Sprintf(bootstrap.PathNodeMachineAccountInfoPriv, nodeID) 250 err := write(path, info) 251 if err != nil { 252 return err 253 } 254 255 return nil 256 } 257 258 // WriteSecretsDBEncryptionKeyFiles writes secret db encryption keys to private 259 // node info directory. 260 func WriteSecretsDBEncryptionKeyFiles(nodeInfos []bootstrap.NodeInfo, write WriteFileFunc) error { 261 262 for _, nodeInfo := range nodeInfos { 263 264 // generate an encryption key for the node 265 encryptionKey, err := GenerateSecretsDBEncryptionKey() 266 if err != nil { 267 return err 268 } 269 270 path := fmt.Sprintf(bootstrap.PathSecretsEncryptionKey, nodeInfo.NodeID) 271 err = write(path, encryptionKey) 272 if err != nil { 273 return err 274 } 275 } 276 return nil 277 } 278 279 // WriteStakingNetworkingKeyFiles writes staking and networking keys to private 280 // node info files. 281 func WriteStakingNetworkingKeyFiles(nodeInfos []bootstrap.NodeInfo, write WriteJSONFileFunc) error { 282 283 for _, nodeInfo := range nodeInfos { 284 285 // retrieve private representation of the node 286 private, err := nodeInfo.Private() 287 if err != nil { 288 return err 289 } 290 291 path := fmt.Sprintf(bootstrap.PathNodeInfoPriv, nodeInfo.NodeID) 292 err = write(path, private) 293 if err != nil { 294 return err 295 } 296 } 297 298 return nil 299 }