github.com/ethersphere/bee/v2@v2.2.0/pkg/keystore/file/key.go (about)

     1  // Copyright 2020 The Swarm Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package file
     6  
     7  import (
     8  	"bytes"
     9  	"crypto/aes"
    10  	"crypto/cipher"
    11  	"crypto/ecdsa"
    12  	"crypto/elliptic"
    13  	"crypto/rand"
    14  	"encoding/hex"
    15  	"encoding/json"
    16  	"fmt"
    17  	"io"
    18  
    19  	"github.com/btcsuite/btcd/btcec/v2"
    20  	"github.com/ethersphere/bee/v2/pkg/crypto"
    21  	"github.com/ethersphere/bee/v2/pkg/keystore"
    22  	"github.com/google/uuid"
    23  	"golang.org/x/crypto/scrypt"
    24  	"golang.org/x/crypto/sha3"
    25  )
    26  
    27  var _ keystore.Service = (*Service)(nil)
    28  
    29  const (
    30  	keyHeaderKDF = "scrypt"
    31  	keyVersion   = 3
    32  
    33  	scryptN     = 1 << 15
    34  	scryptR     = 8
    35  	scryptP     = 1
    36  	scryptDKLen = 32
    37  )
    38  
    39  // This format is compatible with Ethereum JSON v3 key file format.
    40  type encryptedKey struct {
    41  	Address string    `json:"address"`
    42  	Crypto  keyCripto `json:"crypto"`
    43  	Version int       `json:"version"`
    44  	Id      string    `json:"id"`
    45  }
    46  
    47  type keyCripto struct {
    48  	Cipher       string       `json:"cipher"`
    49  	CipherText   string       `json:"ciphertext"`
    50  	CipherParams cipherParams `json:"cipherparams"`
    51  	KDF          string       `json:"kdf"`
    52  	KDFParams    kdfParams    `json:"kdfparams"`
    53  	MAC          string       `json:"mac"`
    54  }
    55  
    56  type cipherParams struct {
    57  	IV string `json:"iv"`
    58  }
    59  
    60  type kdfParams struct {
    61  	N     int    `json:"n"`
    62  	R     int    `json:"r"`
    63  	P     int    `json:"p"`
    64  	DKLen int    `json:"dklen"`
    65  	Salt  string `json:"salt"`
    66  }
    67  
    68  func encryptKey(k *ecdsa.PrivateKey, password string, edg keystore.EDG) ([]byte, error) {
    69  	data, err := edg.Encode(k)
    70  	if err != nil {
    71  		return nil, err
    72  	}
    73  	kc, err := encryptData(data, []byte(password))
    74  	if err != nil {
    75  		return nil, err
    76  	}
    77  	var addr []byte
    78  	switch k.PublicKey.Curve {
    79  	case btcec.S256():
    80  		a, err := crypto.NewEthereumAddress(k.PublicKey)
    81  		if err != nil {
    82  			return nil, err
    83  		}
    84  		addr = a
    85  	case elliptic.P256():
    86  		addr = elliptic.Marshal(elliptic.P256(), k.PublicKey.X, k.PublicKey.Y)
    87  	default:
    88  		return nil, fmt.Errorf("unsupported curve: %v", k.PublicKey.Curve)
    89  	}
    90  	return json.Marshal(encryptedKey{
    91  		Address: hex.EncodeToString(addr),
    92  		Crypto:  *kc,
    93  		Version: keyVersion,
    94  		Id:      uuid.NewString(),
    95  	})
    96  }
    97  
    98  func decryptKey(data []byte, password string, edg keystore.EDG) (*ecdsa.PrivateKey, error) {
    99  	var k encryptedKey
   100  	if err := json.Unmarshal(data, &k); err != nil {
   101  		return nil, err
   102  	}
   103  	if k.Version != keyVersion {
   104  		return nil, fmt.Errorf("unsupported key version: %v", k.Version)
   105  	}
   106  	d, err := decryptData(k.Crypto, password)
   107  	if err != nil {
   108  		return nil, err
   109  	}
   110  	return edg.Decode(d)
   111  }
   112  
   113  func encryptData(data, password []byte) (*keyCripto, error) {
   114  	salt := make([]byte, 32)
   115  	if _, err := io.ReadFull(rand.Reader, salt); err != nil {
   116  		return nil, fmt.Errorf("read random data: %w", err)
   117  	}
   118  	derivedKey, err := scrypt.Key(password, salt, scryptN, scryptR, scryptP, scryptDKLen)
   119  	if err != nil {
   120  		return nil, err
   121  	}
   122  	encryptKey := derivedKey[:16]
   123  
   124  	iv := make([]byte, aes.BlockSize)
   125  	if _, err := io.ReadFull(rand.Reader, iv); err != nil {
   126  		return nil, fmt.Errorf("read random data: %w", err)
   127  	}
   128  	cipherText, err := aesCTRXOR(encryptKey, data, iv)
   129  	if err != nil {
   130  		return nil, err
   131  	}
   132  	mac, err := crypto.LegacyKeccak256(append(derivedKey[16:32], cipherText...))
   133  	if err != nil {
   134  		return nil, err
   135  	}
   136  
   137  	return &keyCripto{
   138  		Cipher:     "aes-128-ctr",
   139  		CipherText: hex.EncodeToString(cipherText),
   140  		CipherParams: cipherParams{
   141  			IV: hex.EncodeToString(iv),
   142  		},
   143  		KDF: keyHeaderKDF,
   144  		KDFParams: kdfParams{
   145  			N:     scryptN,
   146  			R:     scryptR,
   147  			P:     scryptP,
   148  			DKLen: scryptDKLen,
   149  			Salt:  hex.EncodeToString(salt),
   150  		},
   151  		MAC: hex.EncodeToString(mac[:]),
   152  	}, nil
   153  }
   154  
   155  func decryptData(v keyCripto, password string) ([]byte, error) {
   156  	if v.Cipher != "aes-128-ctr" {
   157  		return nil, fmt.Errorf("unsupported cipher: %v", v.Cipher)
   158  	}
   159  
   160  	mac, err := hex.DecodeString(v.MAC)
   161  	if err != nil {
   162  		return nil, fmt.Errorf("hex decode mac: %w", err)
   163  	}
   164  	cipherText, err := hex.DecodeString(v.CipherText)
   165  	if err != nil {
   166  		return nil, fmt.Errorf("hex decode cipher text: %w", err)
   167  	}
   168  	derivedKey, err := getKDFKey(v, []byte(password))
   169  	if err != nil {
   170  		return nil, err
   171  	}
   172  	calculatedMAC := sha3.Sum256(append(derivedKey[16:32], cipherText...))
   173  	if !bytes.Equal(calculatedMAC[:], mac) {
   174  		// if this fails we might be trying to load an ethereum V3 keyfile
   175  		calculatedMACEth, err := crypto.LegacyKeccak256(append(derivedKey[16:32], cipherText...))
   176  		if err != nil {
   177  			return nil, err
   178  		}
   179  		if !bytes.Equal(calculatedMACEth[:], mac) {
   180  			return nil, keystore.ErrInvalidPassword
   181  		}
   182  	}
   183  
   184  	iv, err := hex.DecodeString(v.CipherParams.IV)
   185  	if err != nil {
   186  		return nil, fmt.Errorf("hex decode IV cipher parameter: %w", err)
   187  	}
   188  	data, err := aesCTRXOR(derivedKey[:16], cipherText, iv)
   189  	if err != nil {
   190  		return nil, err
   191  	}
   192  	return data, nil
   193  }
   194  
   195  func aesCTRXOR(key, inText, iv []byte) ([]byte, error) {
   196  	aesBlock, err := aes.NewCipher(key)
   197  	if err != nil {
   198  		return nil, err
   199  	}
   200  	stream := cipher.NewCTR(aesBlock, iv)
   201  	outText := make([]byte, len(inText))
   202  	stream.XORKeyStream(outText, inText)
   203  	return outText, nil
   204  }
   205  
   206  func getKDFKey(v keyCripto, password []byte) ([]byte, error) {
   207  	if v.KDF != keyHeaderKDF {
   208  		return nil, fmt.Errorf("unsupported KDF: %s", v.KDF)
   209  	}
   210  	salt, err := hex.DecodeString(v.KDFParams.Salt)
   211  	if err != nil {
   212  		return nil, fmt.Errorf("hex decode salt: %w", err)
   213  	}
   214  	return scrypt.Key(
   215  		password,
   216  		salt,
   217  		v.KDFParams.N,
   218  		v.KDFParams.R,
   219  		v.KDFParams.P,
   220  		v.KDFParams.DKLen,
   221  	)
   222  }