github.com/theQRL/go-zond@v0.2.1/accounts/keystore/key.go (about)

     1  // Copyright 2014 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-ethereum library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package keystore
    18  
    19  import (
    20  	"encoding/json"
    21  	"fmt"
    22  	"os"
    23  	"path/filepath"
    24  	"time"
    25  
    26  	"github.com/google/uuid"
    27  	"github.com/theQRL/go-qrllib/dilithium"
    28  	"github.com/theQRL/go-zond/accounts"
    29  	"github.com/theQRL/go-zond/common"
    30  	"github.com/theQRL/go-zond/crypto/pqcrypto"
    31  )
    32  
    33  const (
    34  	version = 3
    35  )
    36  
    37  type Key struct {
    38  	Id uuid.UUID // Version 4 "random" for unique id not derived from key data
    39  	// to simplify lookups we also store the address
    40  	Address common.Address
    41  	// we only store seed as pubkey/address & private key can be derived from it
    42  	// seed in this struct is always in plaintext
    43  	Dilithium *dilithium.Dilithium
    44  }
    45  
    46  type keyStore interface {
    47  	// Loads and decrypts the key from disk.
    48  	GetKey(addr common.Address, filename string, auth string) (*Key, error)
    49  	// Writes and encrypts the key.
    50  	StoreKey(filename string, k *Key, auth string) error
    51  	// Joins filename with the key directory unless it is already absolute.
    52  	JoinPath(filename string) string
    53  }
    54  
    55  type plainKeyJSON struct {
    56  	Address string `json:"address"`
    57  	HexSeed string `json:"hexSeed"`
    58  	Id      string `json:"id"`
    59  	Version int    `json:"version"`
    60  }
    61  
    62  type encryptedKeyJSONV3 struct {
    63  	Address string     `json:"address"`
    64  	Crypto  CryptoJSON `json:"crypto"`
    65  	Id      string     `json:"id"`
    66  	Version int        `json:"version"`
    67  }
    68  
    69  type CryptoJSON struct {
    70  	Cipher       string                 `json:"cipher"`
    71  	CipherText   string                 `json:"ciphertext"`
    72  	CipherParams cipherparamsJSON       `json:"cipherparams"`
    73  	KDF          string                 `json:"kdf"`
    74  	KDFParams    map[string]interface{} `json:"kdfparams"`
    75  	MAC          string                 `json:"mac"`
    76  }
    77  
    78  type cipherparamsJSON struct {
    79  	IV string `json:"iv"`
    80  }
    81  
    82  func (k *Key) MarshalJSON() (j []byte, err error) {
    83  	seed := k.Dilithium.GetSeed()
    84  	jStruct := plainKeyJSON{
    85  		fmt.Sprintf("%#x", k.Address),
    86  		common.Bytes2Hex(seed[:]),
    87  		k.Id.String(),
    88  		version,
    89  	}
    90  	j, err = json.Marshal(jStruct)
    91  	return j, err
    92  }
    93  
    94  func (k *Key) UnmarshalJSON(j []byte) (err error) {
    95  	keyJSON := new(plainKeyJSON)
    96  	err = json.Unmarshal(j, &keyJSON)
    97  	if err != nil {
    98  		return err
    99  	}
   100  
   101  	u := new(uuid.UUID)
   102  	*u, err = uuid.Parse(keyJSON.Id)
   103  	if err != nil {
   104  		return err
   105  	}
   106  	k.Id = *u
   107  	addr, err := common.NewAddressFromString(keyJSON.Address)
   108  	if err != nil {
   109  		return err
   110  	}
   111  
   112  	k.Address = addr
   113  	k.Dilithium, err = pqcrypto.HexToDilithium(keyJSON.HexSeed)
   114  	if err != nil {
   115  		return err
   116  	}
   117  
   118  	return nil
   119  }
   120  
   121  func newKeyFromDilithium(d *dilithium.Dilithium) *Key {
   122  	id, err := uuid.NewRandom()
   123  	if err != nil {
   124  		panic(fmt.Sprintf("Could not create random uuid: %v", err))
   125  	}
   126  	key := &Key{
   127  		Id:        id,
   128  		Address:   d.GetAddress(),
   129  		Dilithium: d,
   130  	}
   131  	return key
   132  }
   133  
   134  func newKey() (*Key, error) {
   135  	d, err := dilithium.New()
   136  	if err != nil {
   137  		return nil, err
   138  	}
   139  	return newKeyFromDilithium(d), nil
   140  }
   141  
   142  func storeNewKey(ks keyStore, auth string) (*Key, accounts.Account, error) {
   143  	key, err := newKey()
   144  	if err != nil {
   145  		return nil, accounts.Account{}, err
   146  	}
   147  	a := accounts.Account{
   148  		Address: key.Address,
   149  		URL:     accounts.URL{Scheme: KeyStoreScheme, Path: ks.JoinPath(keyFileName(key.Address))},
   150  	}
   151  	if err := ks.StoreKey(a.URL.Path, key, auth); err != nil {
   152  		zeroKey(&key.Dilithium)
   153  		return nil, a, err
   154  	}
   155  	return key, a, err
   156  }
   157  
   158  func writeTemporaryKeyFile(file string, content []byte) (string, error) {
   159  	// Create the keystore directory with appropriate permissions
   160  	// in case it is not present yet.
   161  	const dirPerm = 0700
   162  	if err := os.MkdirAll(filepath.Dir(file), dirPerm); err != nil {
   163  		return "", err
   164  	}
   165  	// Atomic write: create a temporary hidden file first
   166  	// then move it into place. TempFile assigns mode 0600.
   167  	f, err := os.CreateTemp(filepath.Dir(file), "."+filepath.Base(file)+".tmp")
   168  	if err != nil {
   169  		return "", err
   170  	}
   171  	if _, err := f.Write(content); err != nil {
   172  		f.Close()
   173  		os.Remove(f.Name())
   174  		return "", err
   175  	}
   176  	f.Close()
   177  	return f.Name(), nil
   178  }
   179  
   180  func writeKeyFile(file string, content []byte) error {
   181  	name, err := writeTemporaryKeyFile(file, content)
   182  	if err != nil {
   183  		return err
   184  	}
   185  	return os.Rename(name, file)
   186  }
   187  
   188  // keyFileName implements the naming convention for keyfiles:
   189  // UTC--<created_at UTC ISO8601>-<z-prefixed address hex>
   190  func keyFileName(keyAddr common.Address) string {
   191  	ts := time.Now().UTC()
   192  	return fmt.Sprintf("UTC--%s--%#x", toISO8601(ts), keyAddr)
   193  }
   194  
   195  func toISO8601(t time.Time) string {
   196  	var tz string
   197  	name, offset := t.Zone()
   198  	if name == "UTC" {
   199  		tz = "Z"
   200  	} else {
   201  		tz = fmt.Sprintf("%03d00", offset/3600)
   202  	}
   203  	return fmt.Sprintf("%04d-%02d-%02dT%02d-%02d-%02d.%09d%s",
   204  		t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second(), t.Nanosecond(), tz)
   205  }