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 //}