github.com/futurehomeno/fimpgo@v1.14.0/security/key-store.go (about)

     1  package security
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	log "github.com/sirupsen/logrus"
     7  	"io/ioutil"
     8  	"os"
     9  	"time"
    10  )
    11  
    12  const KeyTypePublic = "pub"
    13  const KeyTypePrivate = "private"
    14  const KeyTypeSymmetric = "sym"
    15  
    16  const AlgEcdsa256 = "ES256"
    17  
    18  type KeyRecord struct {
    19  	UserId        string
    20  	DeviceId      string // Client device ID (mobile phone id)
    21  	Algorithm     string // ES256
    22  	KeyType       string // public/private/symmetric
    23  	SerializedKey string // serialized key
    24  	EcdsaKey      *EcdsaKey
    25  	AddedAt       string // SerializedKey added timestamp
    26  }
    27  
    28  type KeyStore struct {
    29  	keyStore         []KeyRecord
    30  	keyStoreFilePath string
    31  	isPrivate        bool // private store should restrict other application from reading key store file.
    32  }
    33  
    34  func NewKeyStore(keyStoreFilePath string,isPrivate bool) *KeyStore {
    35  	if keyStoreFilePath == "" {
    36  		keyStoreFilePath = "/var/lib/futurehome/hub/pub_key_store.json"
    37  	}
    38  	return &KeyStore{keyStoreFilePath: keyStoreFilePath,isPrivate: isPrivate}
    39  }
    40  
    41  // full username is a string which identifies a user on given device
    42  func (cs *KeyStore) CheckIfUserHasKey(userId, deviceId string) bool {
    43  	for i := range cs.keyStore {
    44  		if cs.keyStore[i].DeviceId == deviceId && cs.keyStore[i].UserId == userId {
    45  			return true
    46  		}
    47  	}
    48  	return false
    49  }
    50  
    51  func (cs *KeyStore) AddSerializedKey(user, device, key, keyType, algo string) error {
    52  	cs.keyStore = append(cs.keyStore, KeyRecord{
    53  		UserId:        user,
    54  		DeviceId:      device,
    55  		SerializedKey: key,
    56  		KeyType:       keyType,
    57  		Algorithm:     algo,
    58  		AddedAt:       time.Now().Format(time.RFC3339),
    59  	})
    60  	return cs.SaveToDisk()
    61  }
    62  
    63  func (cs *KeyStore) UpdateSerializedKey(userId, deviceId, key, keyType, algo string) (bool,error) {
    64  	for i := range cs.keyStore {
    65  		if cs.keyStore[i].DeviceId == deviceId && cs.keyStore[i].UserId == userId && cs.keyStore[i].KeyType == keyType && cs.keyStore[i].Algorithm == algo {
    66  			cs.keyStore[i].SerializedKey = key
    67  			cs.keyStore[i].AddedAt = time.Now().Format(time.RFC3339)
    68  			return true,cs.SaveToDisk()
    69  
    70  		}
    71  	}
    72  	return false,nil
    73  }
    74  
    75  func (cs *KeyStore) UpsertSerializedKey(userId, deviceId, key, keyType, algo string) error {
    76  	keyExists , err := cs.UpdateSerializedKey(userId,deviceId,key,keyType,algo)
    77  	if !keyExists {
    78  		err = cs.AddSerializedKey(userId,deviceId,key,keyType,algo)
    79  	}
    80  	return err
    81  }
    82  
    83  
    84  func (cs *KeyStore) GetKey(userId, deviceId, keyType string) *KeyRecord {
    85  	for i := range cs.keyStore {
    86  		if cs.keyStore[i].DeviceId == deviceId && cs.keyStore[i].UserId == userId && cs.keyStore[i].KeyType == keyType {
    87  			return &cs.keyStore[i]
    88  		}
    89  	}
    90  	return nil
    91  }
    92  
    93  func (cs *KeyStore) GetEcdsaKey(userId, deviceId ,keyType string) (*EcdsaKey,error) {
    94  	for i := range cs.keyStore {
    95  		if cs.keyStore[i].DeviceId == deviceId && cs.keyStore[i].UserId == userId && cs.keyStore[i].Algorithm == AlgEcdsa256 && cs.keyStore[i].KeyType == keyType{
    96  			if cs.keyStore[i].EcdsaKey == nil {
    97  				if cs.keyStore[i].SerializedKey == "" {
    98  					log.Warn("<kstore> Empty key string")
    99  					return nil,fmt.Errorf("empty key string")
   100  				}else {
   101  					cs.keyStore[i].EcdsaKey = NewEcdsaKey()
   102  					var err error
   103  					if cs.keyStore[i].KeyType == KeyTypePrivate {
   104  						err = cs.keyStore[i].EcdsaKey.ImportX509PrivateKey(cs.keyStore[i].SerializedKey)
   105  					}else if cs.keyStore[i].KeyType == KeyTypePublic {
   106  						err = cs.keyStore[i].EcdsaKey.ImportX509PublicKey(cs.keyStore[i].SerializedKey)
   107  					}else {
   108  						return nil,fmt.Errorf("unknown key type %s",keyType)
   109  					}
   110  					if err != nil {
   111  						return nil, err
   112  					}
   113  					return cs.keyStore[i].EcdsaKey,nil
   114  				}
   115  			}else{
   116  				return cs.keyStore[i].EcdsaKey,nil
   117  			}
   118  		}
   119  	}
   120  	return nil,fmt.Errorf("key not found")
   121  }
   122  
   123  
   124  //
   125  func (cs *KeyStore) GetAllUserKeys(userId string) []KeyRecord {
   126  	var result []KeyRecord
   127  	for i := range cs.keyStore {
   128  		if cs.keyStore[i].UserId == userId {
   129  			result = append(result, cs.keyStore[i])
   130  		}
   131  	}
   132  	return result
   133  }
   134  
   135  func (cs *KeyStore) SaveToDisk() error {
   136  	bpayload, err := json.Marshal(cs.keyStore)
   137  	var mode os.FileMode
   138  	if cs.isPrivate {
   139  		mode = 0600
   140  	}else {
   141  		mode = 0664
   142  	}
   143  	err = ioutil.WriteFile(cs.keyStoreFilePath, bpayload, mode)
   144  	if err != nil {
   145  		return err
   146  	}
   147  	return err
   148  	return nil
   149  }
   150  
   151  func (cs *KeyStore) LoadFromDisk() error {
   152  	configFileBody, err := ioutil.ReadFile(cs.keyStoreFilePath)
   153  	if err != nil {
   154  		return err
   155  	}
   156  	err = json.Unmarshal(configFileBody, &cs.keyStore)
   157  	if err != nil {
   158  		return err
   159  	}
   160  	return nil
   161  }
   162  
   163  // An app should call the method to authenticate message
   164  //func (cs *KeyStore) IsMessageAuthenticated(msg *fimpgo.FimpMessage) (bool) {
   165  //	//1. Extract username and signature from user message
   166  //	//2. Query public key from local key store
   167  //	//3. Validate signature using public key
   168  //
   169  //}