decred.org/dcrdex@v1.0.5/server/cmd/dcrdex/key.go (about)

     1  // This code is available on the terms of the project LICENSE.md file,
     2  // also available online at https://blueoakcouncil.org/license/1.0.0.
     3  
     4  package main
     5  
     6  import (
     7  	"errors"
     8  	"fmt"
     9  	"os"
    10  
    11  	"decred.org/dcrdex/dex/encode"
    12  	"decred.org/dcrdex/dex/encrypt"
    13  	"github.com/decred/dcrd/dcrec/secp256k1/v4"
    14  )
    15  
    16  func dexKey(path string, pass []byte) (*secp256k1.PrivateKey, error) {
    17  	var privKey *secp256k1.PrivateKey
    18  	if _, err := os.Stat(path); os.IsNotExist(err) {
    19  		log.Infof("Creating new DEX signing key file at %s...", path)
    20  		privKey, err = createAndStoreKey(path, pass)
    21  		if err != nil {
    22  			return nil, fmt.Errorf("failed to load DEX private key from file %s: %v",
    23  				path, err)
    24  		}
    25  	} else {
    26  		log.Infof("Loading DEX signing key from %s...", path)
    27  		privKey, err = loadKeyFile(path, pass)
    28  		if err != nil {
    29  			return nil, fmt.Errorf("failed to load DEX private key from file %s: %v",
    30  				path, err)
    31  		}
    32  	}
    33  	return privKey, nil
    34  }
    35  
    36  func loadKeyFile(path string, pass []byte) (*secp256k1.PrivateKey, error) {
    37  	// Load and decrypt it.
    38  	pkFileBuffer, err := os.ReadFile(path)
    39  	if err != nil {
    40  		return nil, fmt.Errorf("ReadFile: %v", err)
    41  	}
    42  
    43  	ver, pushes, err := encode.DecodeBlob(pkFileBuffer)
    44  	if err != nil {
    45  		return nil, fmt.Errorf("failed to unmarshal DEX signing key data: %v", err)
    46  	}
    47  	if ver != 0 {
    48  		return nil, fmt.Errorf("unrecognized key file version %d: %v", ver, err)
    49  	}
    50  	if len(pushes) != 2 {
    51  		return nil, fmt.Errorf("invalid signing key file, "+
    52  			"containing %d data pushes instead of 2", len(pushes))
    53  	}
    54  	keyParams := pushes[0]
    55  	encKey := pushes[1]
    56  
    57  	crypter, err := encrypt.Deserialize(pass, keyParams)
    58  	if err != nil {
    59  		return nil, err
    60  	}
    61  
    62  	keyB, err := crypter.Decrypt(encKey)
    63  	if err != nil {
    64  		return nil, err
    65  	}
    66  	// secp256k1.PrivKeyFromBytes() but don't trust that the DB has a valid key.
    67  	var priv secp256k1.PrivateKey
    68  	if overflow := priv.Key.SetByteSlice(keyB); overflow || priv.Key.IsZero() {
    69  		return nil, errors.New("invalid decrypted private key bytes")
    70  	}
    71  	return &priv, nil
    72  }
    73  
    74  func createAndStoreKey(path string, pass []byte) (*secp256k1.PrivateKey, error) {
    75  	// Disallow an empty password.
    76  	if len(pass) == 0 {
    77  		return nil, fmt.Errorf("empty password")
    78  	}
    79  	// Do not overwrite existing key files.
    80  	if _, err := os.Stat(path); err == nil {
    81  		return nil, fmt.Errorf("key file exists")
    82  	}
    83  
    84  	// Create and store a new key.
    85  	privKey, err := secp256k1.GeneratePrivateKey()
    86  	if err != nil {
    87  		return nil, fmt.Errorf("failed to generate DEX signing key: %v", err)
    88  	}
    89  
    90  	// Encrypt the private key.
    91  	crypter := encrypt.NewCrypter(pass)
    92  	keyParams := crypter.Serialize()
    93  	encKey, err := crypter.Encrypt(privKey.Serialize())
    94  	if err != nil {
    95  		return nil, fmt.Errorf("failed to encrypt DEX signing key: %v", err)
    96  	}
    97  	// Check a round trip with this key data.
    98  	_, err = crypter.Decrypt(encKey)
    99  	if err != nil {
   100  		return nil, fmt.Errorf("failed to decrypt DEX signing key: %v", err)
   101  	}
   102  
   103  	// Store it.
   104  	data := encode.BuildyBytes{0}.AddData(keyParams).AddData(encKey)
   105  	err = os.WriteFile(path, data, 0644)
   106  	if err != nil {
   107  		return nil, fmt.Errorf("failed to write DEX signing key: %v", err)
   108  	}
   109  
   110  	return privKey, nil
   111  }