go.dedis.ch/onet/v4@v4.0.0-pre1/app/config.go (about) 1 package app 2 3 import ( 4 "bytes" 5 "crypto/tls" 6 "fmt" 7 "io" 8 "io/ioutil" 9 "os" 10 "strconv" 11 "strings" 12 13 "github.com/BurntSushi/toml" 14 "go.dedis.ch/kyber/v4" 15 "go.dedis.ch/kyber/v4/suites" 16 "go.dedis.ch/kyber/v4/util/encoding" 17 "go.dedis.ch/onet/v4" 18 "go.dedis.ch/onet/v4/log" 19 "go.dedis.ch/onet/v4/network" 20 "golang.org/x/xerrors" 21 ) 22 23 // CothorityConfig is the configuration structure of the cothority daemon. 24 // - Suite: The cryptographic suite 25 // - Public: The public key 26 // - Private: The Private key 27 // - Address: The external address of the conode, used by others to connect to this one 28 // - ListenAddress: The address this conode is listening on 29 // - Description: The description 30 // - URL: The URL where this server can be contacted externally. 31 // - WebSocketTLSCertificate: TLS certificate for the WebSocket 32 // - WebSocketTLSCertificateKey: TLS certificate key for the WebSocket 33 type CothorityConfig struct { 34 Suite string 35 Public string 36 Services map[string]ServiceConfig 37 Private string 38 Address network.Address 39 ListenAddress string 40 Description string 41 URL string 42 WebSocketTLSCertificate CertificateURL 43 WebSocketTLSCertificateKey CertificateURL 44 } 45 46 // ServiceConfig is the configuration of a specific service to override 47 // default parameters as the key pair 48 type ServiceConfig struct { 49 Suite string 50 Public string 51 Private string 52 } 53 54 // Save will save this CothorityConfig to the given file name. It 55 // will return an error if the file couldn't be created or if 56 // there is an error in the encoding. 57 func (hc *CothorityConfig) Save(file string) error { 58 fd, err := os.OpenFile(file, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600) 59 if err != nil { 60 return xerrors.Errorf("opening config file: %v", err) 61 } 62 fd.WriteString("# This file contains your private key.\n") 63 fd.WriteString("# Do not give it away lightly!\n") 64 err = toml.NewEncoder(fd).Encode(hc) 65 if err != nil { 66 return xerrors.Errorf("toml encoding: %v", err) 67 } 68 return nil 69 } 70 71 // LoadCothority loads a conode config from the given file. 72 func LoadCothority(file string) (*CothorityConfig, error) { 73 hc := &CothorityConfig{} 74 _, err := toml.DecodeFile(file, hc) 75 if err != nil { 76 return nil, xerrors.Errorf("toml decoding: %v", err) 77 } 78 79 // Backwards compatibility with configs before we included the suite name 80 if hc.Suite == "" { 81 hc.Suite = "Ed25519" 82 } 83 return hc, nil 84 } 85 86 // GetServerIdentity will convert a CothorityConfig into a *network.ServerIdentity. 87 // It can give an error if there is a problem parsing the strings from the CothorityConfig. 88 func (hc *CothorityConfig) GetServerIdentity() (*network.ServerIdentity, error) { 89 suite, err := suites.Find(hc.Suite) 90 if err != nil { 91 return nil, xerrors.Errorf("kyber suite: %v", err) 92 } 93 94 // Try to decode the Hex values 95 private, err := encoding.StringHexToScalar(suite, hc.Private) 96 if err != nil { 97 return nil, xerrors.Errorf("parsing private key: %v", err) 98 } 99 point, err := encoding.StringHexToPoint(suite, hc.Public) 100 if err != nil { 101 return nil, xerrors.Errorf("parsing public key: %v", err) 102 } 103 si := network.NewServerIdentity(point, hc.Address) 104 si.SetPrivate(private) 105 si.Description = hc.Description 106 si.ServiceIdentities = parseServiceConfig(hc.Services) 107 if hc.WebSocketTLSCertificateKey != "" { 108 if hc.URL != "" { 109 si.URL = strings.Replace(hc.URL, "http://", "https://", 0) 110 } else { 111 p, err := strconv.Atoi(si.Address.Port()) 112 if err != nil { 113 return nil, xerrors.Errorf("port conversion: %v") 114 } 115 si.URL = fmt.Sprintf("https://%s:%d", si.Address.Host(), p+1) 116 } 117 } else { 118 si.URL = hc.URL 119 } 120 121 return si, nil 122 } 123 124 // ParseCothority parses the config file into a CothorityConfig. 125 // It returns the CothorityConfig, the Host so we can already use it, and an error if 126 // the file is inaccessible or has wrong values in it. 127 func ParseCothority(file string) (*CothorityConfig, *onet.Server, error) { 128 hc, err := LoadCothority(file) 129 if err != nil { 130 return nil, nil, xerrors.Errorf("reading config: %v", err) 131 } 132 suite, err := suites.Find(hc.Suite) 133 if err != nil { 134 return nil, nil, xerrors.Errorf("kyber suite: %v", err) 135 } 136 137 si, err := hc.GetServerIdentity() 138 if err != nil { 139 return nil, nil, xerrors.Errorf("parse server identity: %v", err) 140 } 141 142 // Same as `NewServerTCP` if `hc.ListenAddress` is empty 143 server := onet.NewServerTCPWithListenAddr(si, suite, hc.ListenAddress) 144 145 // Set Websocket TLS if possible 146 if hc.WebSocketTLSCertificate != "" && hc.WebSocketTLSCertificateKey != "" { 147 if hc.WebSocketTLSCertificate.CertificateURLType() == File && 148 hc.WebSocketTLSCertificateKey.CertificateURLType() == File { 149 // Use the reloader only when both are files as it doesn't 150 // make sense for string embedded certificates. 151 152 cr, err := onet.NewCertificateReloader( 153 hc.WebSocketTLSCertificate.blobPart(), 154 hc.WebSocketTLSCertificateKey.blobPart(), 155 ) 156 if err != nil { 157 return nil, nil, xerrors.Errorf("certificate: %v", err) 158 } 159 160 server.WebSocket.Lock() 161 server.WebSocket.TLSConfig = &tls.Config{ 162 GetCertificate: cr.GetCertificateFunc(), 163 } 164 server.WebSocket.Unlock() 165 } else { 166 tlsCertificate, err := hc.WebSocketTLSCertificate.Content() 167 if err != nil { 168 return nil, nil, xerrors.Errorf("getting WebSocketTLSCertificate content: %v", err) 169 } 170 tlsCertificateKey, err := hc.WebSocketTLSCertificateKey.Content() 171 if err != nil { 172 return nil, nil, xerrors.Errorf("getting WebSocketTLSCertificateKey content: %v", err) 173 } 174 cert, err := tls.X509KeyPair(tlsCertificate, tlsCertificateKey) 175 if err != nil { 176 return nil, nil, xerrors.Errorf("loading X509KeyPair: %v", err) 177 } 178 179 server.WebSocket.Lock() 180 server.WebSocket.TLSConfig = &tls.Config{ 181 Certificates: []tls.Certificate{cert}, 182 } 183 server.WebSocket.Unlock() 184 } 185 } 186 return hc, server, nil 187 } 188 189 // GroupToml holds the data of the group.toml file. 190 type GroupToml struct { 191 Servers []*ServerToml `toml:"servers"` 192 } 193 194 // NewGroupToml creates a new GroupToml struct from the given ServerTomls. 195 // Currently used together with calling String() on the GroupToml to output 196 // a snippet which can be used to create a Cothority. 197 func NewGroupToml(servers ...*ServerToml) *GroupToml { 198 return &GroupToml{ 199 Servers: servers, 200 } 201 } 202 203 // ServerToml is one entry in the group.toml file describing one server to use for 204 // the cothority. 205 type ServerToml struct { 206 Address network.Address 207 Suite string 208 Public string 209 Description string 210 Services map[string]ServerServiceConfig 211 URL string `toml:"URL,omitempty"` 212 } 213 214 // ServerServiceConfig is a public configuration for a server (i.e. private key 215 // is missing) 216 type ServerServiceConfig struct { 217 Public string 218 Suite string 219 } 220 221 // Group holds the Roster and the server-description. 222 type Group struct { 223 Roster *onet.Roster 224 Description map[*network.ServerIdentity]string 225 } 226 227 // GetDescription returns the description of a ServerIdentity. 228 func (g *Group) GetDescription(e *network.ServerIdentity) string { 229 return g.Description[e] 230 } 231 232 // Toml returns the GroupToml instance of this Group 233 func (g *Group) Toml(suite suites.Suite) (*GroupToml, error) { 234 servers := make([]*ServerToml, len(g.Roster.List)) 235 for i, si := range g.Roster.List { 236 pub, err := encoding.PointToStringHex(suite, si.Public) 237 if err != nil { 238 return nil, xerrors.Errorf("encoding public key: %v", err) 239 } 240 241 services := make(map[string]ServerServiceConfig) 242 for _, sid := range si.ServiceIdentities { 243 suite := onet.ServiceFactory.Suite(sid.Name) 244 245 pub, err := encoding.PointToStringHex(suite, sid.Public) 246 if err != nil { 247 return nil, xerrors.Errorf("encoding service key: %v", err) 248 } 249 250 services[sid.Name] = ServerServiceConfig{Public: pub, Suite: suite.String()} 251 } 252 253 servers[i] = &ServerToml{ 254 Address: si.Address, 255 Suite: suite.String(), 256 Public: pub, 257 Description: si.Description, 258 Services: services, 259 URL: si.URL, 260 } 261 } 262 263 return &GroupToml{Servers: servers}, nil 264 } 265 266 // Save converts the group into a toml structure and save it to the file 267 func (g *Group) Save(suite suites.Suite, filename string) error { 268 gt, err := g.Toml(suite) 269 if err != nil { 270 return xerrors.Errorf("toml encoding: %v", err) 271 } 272 273 return gt.Save(filename) 274 } 275 276 // ReadGroupDescToml reads a group.toml file and returns the list of ServerIdentities 277 // and descriptions in the file. 278 // If the file couldn't be decoded or doesn't hold valid ServerIdentities, 279 // an error is returned. 280 func ReadGroupDescToml(f io.Reader) (*Group, error) { 281 group := &GroupToml{} 282 _, err := toml.DecodeReader(f, group) 283 if err != nil { 284 return nil, xerrors.Errorf("toml decoding: %v", err) 285 } 286 // convert from ServerTomls to entities 287 var entities = make([]*network.ServerIdentity, len(group.Servers)) 288 var descs = make(map[*network.ServerIdentity]string) 289 for i, s := range group.Servers { 290 // Backwards compatibility with old group files. 291 if s.Suite == "" { 292 s.Suite = "Ed25519" 293 } 294 en, err := s.ToServerIdentity() 295 if err != nil { 296 return nil, xerrors.Errorf("server identity encoding: %v", err) 297 } 298 entities[i] = en 299 descs[en] = s.Description 300 } 301 el := onet.NewRoster(entities) 302 return &Group{el, descs}, nil 303 } 304 305 // Save writes the GroupToml definition into the file given by its name. 306 // It will return an error if the file couldn't be created or if writing 307 // to it failed. 308 func (gt *GroupToml) Save(fname string) error { 309 file, err := os.Create(fname) 310 if err != nil { 311 return xerrors.Errorf("creating file: %v", err) 312 } 313 defer file.Close() 314 _, err = file.WriteString(gt.String()) 315 if err != nil { 316 return xerrors.Errorf("writing file: %v", err) 317 } 318 319 return nil 320 } 321 322 // String returns the TOML representation of this GroupToml. 323 func (gt *GroupToml) String() string { 324 var buff bytes.Buffer 325 for _, s := range gt.Servers { 326 if s.Description == "" { 327 s.Description = "Description of your server" 328 } 329 } 330 enc := toml.NewEncoder(&buff) 331 if err := enc.Encode(gt); err != nil { 332 return "Error encoding grouptoml" + err.Error() 333 } 334 return buff.String() 335 } 336 337 // ToServerIdentity converts this ServerToml struct to a ServerIdentity. 338 func (s *ServerToml) ToServerIdentity() (*network.ServerIdentity, error) { 339 suite, err := suites.Find(s.Suite) 340 if err != nil { 341 return nil, xerrors.Errorf("kyber suite: %v", err) 342 } 343 344 pubR := strings.NewReader(s.Public) 345 public, err := encoding.ReadHexPoint(suite, pubR) 346 if err != nil { 347 return nil, xerrors.Errorf("encoding key: %v", err) 348 } 349 si := network.NewServerIdentity(public, s.Address) 350 si.URL = s.URL 351 si.Description = s.Description 352 si.ServiceIdentities = parseServerServiceConfig(s.Services) 353 354 return si, err 355 } 356 357 // NewServerToml takes a public key and an address and returns 358 // the corresponding ServerToml. 359 // If an error occurs, it will be printed to StdErr and nil 360 // is returned. 361 func NewServerToml(suite network.Suite, public kyber.Point, addr network.Address, 362 desc string, services map[string]ServiceConfig) *ServerToml { 363 var buff bytes.Buffer 364 if err := encoding.WriteHexPoint(suite, &buff, public); err != nil { 365 log.Error("Error writing public key") 366 return nil 367 } 368 369 // Keep only the public key 370 publics := make(map[string]ServerServiceConfig) 371 for name, conf := range services { 372 publics[name] = ServerServiceConfig{Public: conf.Public, Suite: conf.Suite} 373 } 374 375 return &ServerToml{ 376 Address: addr, 377 Suite: suite.String(), 378 Public: buff.String(), 379 Description: desc, 380 Services: publics, 381 } 382 } 383 384 // String returns the TOML representation of the ServerToml. 385 func (s *ServerToml) String() string { 386 var buff bytes.Buffer 387 if s.Description == "" { 388 s.Description = "## Put your description here for convenience ##" 389 } 390 enc := toml.NewEncoder(&buff) 391 if err := enc.Encode(s); err != nil { 392 return "## Error encoding server informations ##" + err.Error() 393 } 394 return buff.String() 395 } 396 397 // CertificateURLType represents the type of a CertificateURL. 398 // The supported types are defined as constants of type CertificateURLType. 399 type CertificateURLType string 400 401 // CertificateURL contains the CertificateURLType and the actual URL 402 // certificate, which can be a path leading to a file containing a certificate 403 // or a string directly being a certificate. 404 type CertificateURL string 405 406 const ( 407 // String is a CertificateURL type containing a certificate. 408 String CertificateURLType = "string" 409 // File is a CertificateURL type that contains the path to a file 410 // containing a certificate. 411 File = "file" 412 // InvalidCertificateURLType is an invalid CertificateURL type. 413 InvalidCertificateURLType = "wrong" 414 // DefaultCertificateURLType is the default type when no type is specified 415 DefaultCertificateURLType = File 416 ) 417 418 // typeCertificateURLSep is the separator between the type of the URL 419 // certificate and the string that identifies the certificate (e.g. 420 // filepath, content). 421 const typeCertificateURLSep = "://" 422 423 // certificateURLType converts a string to a CertificateURLType. In case of 424 // failure, it returns InvalidCertificateURLType. 425 func certificateURLType(t string) CertificateURLType { 426 if t == "" { 427 return DefaultCertificateURLType 428 } 429 cuType := CertificateURLType(t) 430 types := []CertificateURLType{String, File} 431 for _, t := range types { 432 if t == cuType { 433 return cuType 434 } 435 } 436 return InvalidCertificateURLType 437 } 438 439 // String returns the CertificateURL as a string. 440 func (cu CertificateURL) String() string { 441 return string(cu) 442 } 443 444 // CertificateURLType returns the CertificateURL type from the CertificateURL. 445 // It returns InvalidCertificateURLType if the CertificateURL is not valid or 446 // if the CertificateURL type is not known. 447 func (cu CertificateURL) CertificateURLType() CertificateURLType { 448 if !cu.Valid() { 449 return InvalidCertificateURLType 450 } 451 return certificateURLType(cu.typePart()) 452 } 453 454 // Valid returns true if the CertificateURL is well formed or false otherwise. 455 func (cu CertificateURL) Valid() bool { 456 vals := strings.Split(string(cu), typeCertificateURLSep) 457 if len(vals) > 2 { 458 return false 459 } 460 cuType := certificateURLType(cu.typePart()) 461 if cuType == InvalidCertificateURLType { 462 return false 463 } 464 465 return true 466 } 467 468 // Content returns the bytes representing the certificate. 469 func (cu CertificateURL) Content() ([]byte, error) { 470 cuType := cu.CertificateURLType() 471 if cuType == String { 472 return []byte(cu.blobPart()), nil 473 } 474 if cuType == File { 475 dat, err := ioutil.ReadFile(cu.blobPart()) 476 if err != nil { 477 return nil, xerrors.Errorf("reading file: %v", err) 478 } 479 return dat, nil 480 } 481 return nil, xerrors.Errorf("Unknown CertificateURL type (%s), cannot get its content", cuType) 482 } 483 484 // typePart returns only the string representing the type of a CertificateURL 485 // (empty string for no type specified) 486 func (cu CertificateURL) typePart() string { 487 vals := strings.Split(string(cu), typeCertificateURLSep) 488 if len(vals) == 1 { 489 return "" 490 } 491 return vals[0] 492 } 493 494 // blobPart returns only the string representing the blob of a CertificateURL 495 // (the content of the certificate, a file path, ...) 496 func (cu CertificateURL) blobPart() string { 497 vals := strings.Split(string(cu), typeCertificateURLSep) 498 if len(vals) == 1 { 499 return vals[0] 500 } 501 return vals[1] 502 } 503 504 // parseServiceConfig takes the map and creates service identities 505 func parseServiceConfig(configs map[string]ServiceConfig) []network.ServiceIdentity { 506 si := []network.ServiceIdentity{} 507 508 for name, sc := range configs { 509 sid, err := parseServiceIdentity(name, sc.Suite, sc.Public, sc.Private) 510 if err != nil { 511 // You might try to parse a toml file for a single service so 512 // you can ignore other pairs 513 log.Lvlf2("Service `%s` not registered. Ignoring the key pair.", name) 514 } else { 515 si = append(si, sid) 516 } 517 } 518 519 return si 520 } 521 522 // parseServerServiceConfig takes the map and creates service identities with only the public key 523 func parseServerServiceConfig(configs map[string]ServerServiceConfig) []network.ServiceIdentity { 524 si := []network.ServiceIdentity{} 525 526 for name, sc := range configs { 527 sid, err := parseServiceIdentity(name, sc.Suite, sc.Public, "") 528 if err != nil { 529 // You might try to parse a toml file for a single service so 530 // you can ignore other pairs 531 log.Lvlf2("Service `%s` not registered. Ignoring the key pair.", name) 532 } else { 533 si = append(si, sid) 534 } 535 } 536 537 return si 538 } 539 540 // parseServiceIdentity creates the service identity 541 func parseServiceIdentity(name string, suiteName string, pub string, priv string) (srvid network.ServiceIdentity, err error) { 542 suite := onet.ServiceFactory.Suite(name) 543 if suite == nil { 544 return srvid, xerrors.Errorf( 545 "Service `%s` has not been registered with a suite", name) 546 } else if suite.String() != suiteName { 547 panic(fmt.Sprintf( 548 "Using suite `%s` but `%s` is required for the `%s` service", suiteName, suite.String(), name)) 549 } 550 551 private := suite.Scalar() 552 if priv != "" { 553 private, err = encoding.StringHexToScalar(suite, priv) 554 if err != nil { 555 return srvid, xerrors.Errorf("parsing `%s` private key: %s", name, err.Error()) 556 } 557 } 558 559 public, err := encoding.StringHexToPoint(suite, pub) 560 if err != nil { 561 return srvid, xerrors.Errorf("parsing `%s` public key: %s", name, err.Error()) 562 } 563 564 si := network.NewServiceIdentity(name, suite, public, private) 565 return si, nil 566 }