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  }