github.com/decred/politeia@v1.4.0/politeiawww/identity.go (about)

     1  // Copyright (c) 2017-2021 The Decred developers
     2  // Use of this source code is governed by an ISC
     3  // license that can be found in the LICENSE file.
     4  
     5  package main
     6  
     7  import (
     8  	"bufio"
     9  	"bytes"
    10  	"encoding/hex"
    11  	"encoding/json"
    12  	"fmt"
    13  	"io"
    14  	"net/http"
    15  	"os"
    16  	"path/filepath"
    17  
    18  	v1 "github.com/decred/politeia/politeiad/api/v1"
    19  	"github.com/decred/politeia/politeiad/api/v1/identity"
    20  	"github.com/decred/politeia/util"
    21  )
    22  
    23  // getIdentity fetches the remote identity from politeiad and saves it to
    24  // disk. politeiawww loads it from disk on future startups during config
    25  // initialization.
    26  func getIdentity(rpcHost, rpcCert, rpcIdentityFile, interactive string) error {
    27  	id, err := remoteIdentity(false, rpcHost, rpcCert)
    28  	if err != nil {
    29  		return err
    30  	}
    31  
    32  	// Pretty print identity.
    33  	log.Infof("Identity fetched from politeiad")
    34  	log.Infof("Key        : %x", id.Key)
    35  	log.Infof("Fingerprint: %v", id.Fingerprint())
    36  
    37  	if interactive == "" {
    38  		// Ask user if we like this identity
    39  		log.Infof("Press enter to save to %v or ctrl-c to abort",
    40  			rpcIdentityFile)
    41  		scanner := bufio.NewScanner(os.Stdin)
    42  		scanner.Scan()
    43  		if err = scanner.Err(); err != nil {
    44  			return err
    45  		}
    46  	} else {
    47  		log.Infof("Saving identity to %v", rpcIdentityFile)
    48  	}
    49  
    50  	// Save identity
    51  	err = os.MkdirAll(filepath.Dir(rpcIdentityFile), 0700)
    52  	if err != nil {
    53  		return err
    54  	}
    55  	err = id.SavePublicIdentity(rpcIdentityFile)
    56  	if err != nil {
    57  		return err
    58  	}
    59  	log.Infof("Identity saved to: %v", rpcIdentityFile)
    60  
    61  	return nil
    62  }
    63  
    64  // remoteIdentity fetches the identity from politeiad.
    65  func remoteIdentity(skipTLSVerify bool, host, cert string) (*identity.PublicIdentity, error) {
    66  	challenge, err := util.Random(v1.ChallengeSize)
    67  	if err != nil {
    68  		return nil, err
    69  	}
    70  	id, err := json.Marshal(v1.Identity{
    71  		Challenge: hex.EncodeToString(challenge),
    72  	})
    73  	if err != nil {
    74  		return nil, err
    75  	}
    76  
    77  	c, err := util.NewHTTPClient(skipTLSVerify, cert)
    78  	if err != nil {
    79  		return nil, err
    80  	}
    81  	r, err := c.Post(host+v1.IdentityRoute, "application/json",
    82  		bytes.NewReader(id))
    83  	if err != nil {
    84  		return nil, err
    85  	}
    86  	defer r.Body.Close()
    87  
    88  	if r.StatusCode != http.StatusOK {
    89  		e, err := util.GetErrorFromJSON(r.Body)
    90  		if err != nil {
    91  			return nil, fmt.Errorf("%v", r.Status)
    92  		}
    93  		return nil, fmt.Errorf("%v: %v", r.Status, e)
    94  	}
    95  
    96  	body, err := io.ReadAll(r.Body)
    97  	if err != nil {
    98  		return nil, err
    99  	}
   100  
   101  	var ir v1.IdentityReply
   102  	err = json.Unmarshal(body, &ir)
   103  	if err != nil {
   104  		return nil, fmt.Errorf("Could node unmarshal IdentityReply: %v",
   105  			err)
   106  	}
   107  
   108  	// Convert and verify server identity
   109  	identity, err := identity.PublicIdentityFromString(ir.PublicKey)
   110  	if err != nil {
   111  		return nil, err
   112  	}
   113  
   114  	err = util.VerifyChallenge(identity, challenge, ir.Response)
   115  	if err != nil {
   116  		return nil, err
   117  	}
   118  
   119  	return identity, nil
   120  }