github.com/status-im/status-go@v1.1.0/server/pairing/common.go (about) 1 package pairing 2 3 import ( 4 "fmt" 5 "io/fs" 6 "io/ioutil" 7 "os" 8 "path/filepath" 9 "reflect" 10 "regexp" 11 "strings" 12 13 "github.com/status-im/status-go/protocol/requests" 14 15 "gopkg.in/go-playground/validator.v9" 16 17 "github.com/status-im/status-go/account/generator" 18 "github.com/status-im/status-go/api" 19 "github.com/status-im/status-go/eth-node/keystore" 20 ) 21 22 func newValidate() (*validator.Validate, error) { 23 var validate = validator.New() 24 var keyUIDPattern = regexp.MustCompile(`^0x[0-9a-fA-F]{64}$`) 25 if err := validate.RegisterValidation("keyuid", func(fl validator.FieldLevel) bool { 26 return keyUIDPattern.MatchString(fl.Field().String()) 27 }); err != nil { 28 return nil, err 29 } 30 31 if err := validate.RegisterValidation("keystorepath", func(fl validator.FieldLevel) bool { 32 keyUIDField := fl.Parent() 33 if keyUIDField.Kind() == reflect.Ptr { 34 keyUIDField = keyUIDField.Elem() 35 } 36 37 keyUID := keyUIDField.FieldByName("KeyUID").String() 38 return strings.HasSuffix(fl.Field().String(), keyUID) 39 }); err != nil { 40 return nil, err 41 } 42 43 return validate, nil 44 } 45 46 func validateKeys(keys map[string][]byte, password string) error { 47 for _, key := range keys { 48 k, err := keystore.DecryptKey(key, password) 49 if err != nil { 50 return err 51 } 52 53 err = generator.ValidateKeystoreExtendedKey(k) 54 if err != nil { 55 return err 56 } 57 } 58 59 return nil 60 } 61 62 func loadKeys(keys map[string][]byte, keyStorePath string) error { 63 fileWalker := func(path string, dirEntry fs.DirEntry, err error) error { 64 if err != nil { 65 return err 66 } 67 68 if dirEntry.IsDir() || filepath.Dir(path) != keyStorePath { 69 return nil 70 } 71 72 rawKeyFile, err := ioutil.ReadFile(path) 73 if err != nil { 74 return fmt.Errorf("invalid account key file: %v", err) 75 } 76 77 keys[dirEntry.Name()] = rawKeyFile 78 79 return nil 80 } 81 82 err := filepath.WalkDir(keyStorePath, fileWalker) 83 if err != nil { 84 return fmt.Errorf("cannot traverse key store folder: %v", err) 85 } 86 87 return nil 88 } 89 90 func validate(s interface{}) error { 91 v, err := newValidate() 92 if err != nil { 93 return err 94 } 95 96 return v.Struct(s) 97 } 98 99 func validateAndVerifyPassword(s interface{}, senderConfig *SenderConfig) error { 100 err := validate(s) 101 if err != nil { 102 return err 103 } 104 105 keys := make(map[string][]byte) 106 err = loadKeys(keys, senderConfig.KeystorePath) 107 if err != nil { 108 return err 109 } 110 111 return validateKeys(keys, senderConfig.Password) 112 } 113 114 func validateReceiverConfig(s interface{}, receiverConfig *ReceiverConfig) error { 115 err := validate(s) 116 if err != nil { 117 return err 118 } 119 120 return receiverConfig.CreateAccount.Validate(&requests.CreateAccountValidation{ 121 AllowEmptyDisplayName: true, 122 AllowEmptyCustomizationColor: true, 123 AllowEmptyPassword: true, 124 }) 125 } 126 127 func emptyDir(dir string) error { 128 // Open the directory 129 d, err := os.Open(dir) 130 if err != nil { 131 return err 132 } 133 defer d.Close() 134 135 // Get all the directory entries 136 entries, err := d.Readdir(-1) 137 if err != nil { 138 return err 139 } 140 141 // Remove all the files and directories 142 for _, entry := range entries { 143 name := entry.Name() 144 if name == "." || name == ".." { 145 continue 146 } 147 path := filepath.Join(dir, name) 148 if entry.IsDir() { 149 err = os.RemoveAll(path) 150 if err != nil { 151 return err 152 } 153 } else { 154 err = os.Remove(path) 155 if err != nil { 156 return err 157 } 158 } 159 } 160 return nil 161 } 162 163 func validateReceivedKeystoreFiles(expectedKeys []string, keys map[string][]byte, password string) error { 164 for _, searchKey := range expectedKeys { 165 found := false 166 for key := range keys { 167 if strings.Contains(key, strings.ToLower(searchKey)) { 168 found = true 169 break 170 } 171 } 172 if !found { 173 return fmt.Errorf("one or more expected keystore files are not found among the sent files") 174 } 175 } 176 177 return validateKeys(keys, password) 178 } 179 180 func validateKeystoreFilesConfig(backend *api.GethStatusBackend, conf interface{}) error { 181 var ( 182 loggedInKeyUID string 183 password string 184 numOfKeypairs int 185 keystorePath string 186 ) 187 188 switch c := conf.(type) { 189 case *KeystoreFilesSenderServerConfig: 190 loggedInKeyUID = c.SenderConfig.LoggedInKeyUID 191 password = c.SenderConfig.Password 192 numOfKeypairs = len(c.SenderConfig.KeypairsToExport) 193 keystorePath = c.SenderConfig.KeystorePath 194 case *KeystoreFilesReceiverClientConfig: 195 loggedInKeyUID = c.ReceiverConfig.LoggedInKeyUID 196 password = c.ReceiverConfig.Password 197 numOfKeypairs = len(c.ReceiverConfig.KeypairsToImport) 198 keystorePath = c.ReceiverConfig.KeystorePath 199 default: 200 return fmt.Errorf("unknown config type: %v", reflect.TypeOf(conf)) 201 } 202 203 accountService := backend.StatusNode().AccountService() 204 if accountService == nil { 205 return fmt.Errorf("cannot resolve accounts service instance") 206 } 207 208 if !accountService.GetMessenger().HasPairedDevices() { 209 return fmt.Errorf("there are no known paired devices") 210 } 211 212 selectedAccount, err := backend.GetActiveAccount() 213 if err != nil { 214 return err 215 } 216 217 if selectedAccount.KeyUID != loggedInKeyUID { 218 return fmt.Errorf("configuration is not meant for the logged in account") 219 } 220 221 if selectedAccount.KeycardPairing == "" { 222 if !accountService.VerifyPassword(password) { 223 return fmt.Errorf("provided password is not correct") 224 } 225 } 226 227 if numOfKeypairs == 0 { 228 return fmt.Errorf("it should be at least a single keypair set a keystore files are transferred for") 229 } 230 231 if keystorePath == "" { 232 return fmt.Errorf("keyStorePath can not be empty") 233 } 234 235 return nil 236 }