github.com/qri-io/qri@v0.10.1-0.20220104210721-c771715036cb/auth/key/keybook.go (about)

     1  package key
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"errors"
     7  	"sync"
     8  
     9  	ic "github.com/libp2p/go-libp2p-core/crypto"
    10  )
    11  
    12  // Book defines the interface for keybook implementations
    13  // which hold the key information
    14  type Book interface {
    15  	// PubKey stores the public key for a key.ID
    16  	PubKey(context.Context, ID) ic.PubKey
    17  
    18  	// AddPubKey stores the public for a key.ID
    19  	AddPubKey(context.Context, ID, ic.PubKey) error
    20  
    21  	// PrivKey returns the private key for a key.ID, if known
    22  	PrivKey(context.Context, ID) ic.PrivKey
    23  
    24  	// AddPrivKey stores the private key for a key.ID
    25  	AddPrivKey(context.Context, ID, ic.PrivKey) error
    26  
    27  	// IDsWithKeys returns all the key IDs stored in the KeyBook
    28  	IDsWithKeys(context.Context) []ID
    29  }
    30  
    31  type memoryKeyBook struct {
    32  	sync.RWMutex // same lock. wont happen a ton.
    33  	pks          map[ID]ic.PubKey
    34  	sks          map[ID]ic.PrivKey
    35  }
    36  
    37  var _ Book = (*memoryKeyBook)(nil)
    38  
    39  func newKeyBook() *memoryKeyBook {
    40  	return &memoryKeyBook{
    41  		pks: map[ID]ic.PubKey{},
    42  		sks: map[ID]ic.PrivKey{},
    43  	}
    44  }
    45  
    46  // IDsWithKeys returns the list of IDs in the KeyBook
    47  func (mkb *memoryKeyBook) IDsWithKeys(_ context.Context) []ID {
    48  	mkb.RLock()
    49  	ps := make([]ID, 0, len(mkb.pks)+len(mkb.sks))
    50  	for p := range mkb.pks {
    51  		ps = append(ps, p)
    52  	}
    53  	for p := range mkb.sks {
    54  		if _, found := mkb.pks[p]; !found {
    55  			ps = append(ps, p)
    56  		}
    57  	}
    58  	mkb.RUnlock()
    59  	return ps
    60  }
    61  
    62  // PubKey returns the public key for a given ID if it exists
    63  func (mkb *memoryKeyBook) PubKey(_ context.Context, k ID) ic.PubKey {
    64  	mkb.RLock()
    65  	pk := mkb.pks[k]
    66  	mkb.RUnlock()
    67  	// TODO(arqu): we ignore the recovery mechanic to avoid magic
    68  	// behavior in above stores. We should revisit once we work out
    69  	// the broader mechanics of managing keys.
    70  	// pk, _ = p.ExtractPublicKey()
    71  	// if err == nil {
    72  	// 	mkb.Lock()
    73  	// 	mkb.pks[p] = pk
    74  	// 	mkb.Unlock()
    75  	// }
    76  	return pk
    77  }
    78  
    79  // AddPubKey inserts a public key for a given ID
    80  func (mkb *memoryKeyBook) AddPubKey(_ context.Context, k ID, pk ic.PubKey) error {
    81  	mkb.Lock()
    82  	mkb.pks[k] = pk
    83  	mkb.Unlock()
    84  	return nil
    85  }
    86  
    87  // PrivKey returns the private key for a given ID if it exists
    88  func (mkb *memoryKeyBook) PrivKey(_ context.Context, k ID) ic.PrivKey {
    89  	mkb.RLock()
    90  	sk := mkb.sks[k]
    91  	mkb.RUnlock()
    92  	return sk
    93  }
    94  
    95  // AddPrivKey inserts a private key for a given ID
    96  func (mkb *memoryKeyBook) AddPrivKey(_ context.Context, k ID, sk ic.PrivKey) error {
    97  	if sk == nil {
    98  		return errors.New("sk is nil (PrivKey)")
    99  	}
   100  
   101  	mkb.Lock()
   102  	mkb.sks[k] = sk
   103  	mkb.Unlock()
   104  	return nil
   105  }
   106  
   107  // MarshalJSON implements the JSON marshal interface
   108  func (mkb *memoryKeyBook) MarshalJSON() ([]byte, error) {
   109  	mkb.RLock()
   110  	res := map[string]interface{}{}
   111  	pubKeys := map[string]string{}
   112  	privKeys := map[string]string{}
   113  	for k, v := range mkb.pks {
   114  		byteKey, err := ic.MarshalPublicKey(v)
   115  		if err != nil {
   116  			// skip/don't marshal ill formed keys
   117  			log.Debugf("keybook: failed to marshal key: %q", err.Error())
   118  			continue
   119  		}
   120  		pubKeys[k.Pretty()] = ic.ConfigEncodeKey(byteKey)
   121  	}
   122  	for k, v := range mkb.sks {
   123  		byteKey, err := ic.MarshalPrivateKey(v)
   124  		if err != nil {
   125  			// skip/don't marshal ill formed keys
   126  			log.Debugf("keybook: failed to marshal key: %q", err.Error())
   127  			continue
   128  		}
   129  		privKeys[k.Pretty()] = ic.ConfigEncodeKey(byteKey)
   130  	}
   131  
   132  	res["public_keys"] = pubKeys
   133  	res["private_keys"] = privKeys
   134  
   135  	mkb.RUnlock()
   136  	return json.Marshal(res)
   137  }
   138  
   139  // UnmarshalJSON implements the JSON unmarshal interface
   140  func (mkb *memoryKeyBook) UnmarshalJSON(data []byte) error {
   141  	ctx := context.Background()
   142  	keyBookJSON := map[string]map[string]string{}
   143  	err := json.Unmarshal(data, &keyBookJSON)
   144  	if err != nil {
   145  		return err
   146  	}
   147  	if pubKeys, ok := keyBookJSON["public_keys"]; ok {
   148  		for k, v := range pubKeys {
   149  			byteKey, err := ic.ConfigDecodeKey(v)
   150  			if err != nil {
   151  				return err
   152  			}
   153  			key, err := ic.UnmarshalPublicKey(byteKey)
   154  			if err != nil {
   155  				return err
   156  			}
   157  			id, err := DecodeID(k)
   158  			if err != nil {
   159  				return err
   160  			}
   161  			err = mkb.AddPubKey(ctx, id, key)
   162  			if err != nil {
   163  				return err
   164  			}
   165  		}
   166  	}
   167  	if privKeys, ok := keyBookJSON["private_keys"]; ok {
   168  		for k, v := range privKeys {
   169  			byteKey, err := ic.ConfigDecodeKey(v)
   170  			if err != nil {
   171  				return err
   172  			}
   173  			key, err := ic.UnmarshalPrivateKey(byteKey)
   174  			if err != nil {
   175  				return err
   176  			}
   177  			id, err := DecodeID(k)
   178  			if err != nil {
   179  				return err
   180  			}
   181  			err = mkb.AddPrivKey(ctx, id, key)
   182  			if err != nil {
   183  				return err
   184  			}
   185  		}
   186  	}
   187  	return nil
   188  }