github.com/greenpau/go-authcrunch@v1.1.4/pkg/kms/key.go (about) 1 // Copyright 2022 Paul Greenberg greenpau@outlook.com 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package kms 16 17 import ( 18 "bytes" 19 "crypto" 20 "crypto/ecdsa" 21 "crypto/elliptic" 22 "crypto/hmac" 23 "crypto/rand" 24 "crypto/rsa" 25 "crypto/x509" 26 "encoding/base64" 27 "encoding/json" 28 "encoding/pem" 29 "fmt" 30 "io/ioutil" 31 "os" 32 "path/filepath" 33 "strings" 34 35 jwtlib "github.com/golang-jwt/jwt/v4" 36 "github.com/greenpau/go-authcrunch/pkg/errors" 37 "github.com/greenpau/go-authcrunch/pkg/shared" 38 "github.com/greenpau/go-authcrunch/pkg/user" 39 ) 40 41 // CryptoKey contains a crypto graphic key and associated metadata. 42 type CryptoKey struct { 43 Config *CryptoKeyConfig `json:"config,omitempty" xml:"config,omitempty" yaml:"config,omitempty"` 44 Sign *CryptoKeyOperator `json:"sign,omitempty" xml:"sign,omitempty" yaml:"sign,omitempty"` 45 Verify *CryptoKeyOperator `json:"verify,omitempty" xml:"verify,omitempty" yaml:"verify,omitempty"` 46 } 47 48 // CryptoKeyTokenOperator represents CryptoKeyOperator token operator. 49 type CryptoKeyTokenOperator struct { 50 ID string `json:"id,omitempty" xml:"id,omitempty" yaml:"id,omitempty"` 51 Name string `json:"name,omitempty" xml:"name,omitempty" yaml:"name,omitempty"` 52 MaxLifetime int `json:"max_lifetime,omitempty" xml:"max_lifetime,omitempty" yaml:"max_lifetime,omitempty"` 53 Methods map[string]interface{} `json:"methods,omitempty" xml:"methods,omitempty" yaml:"methods,omitempty"` 54 PreferredMethods []string `json:"preferred_methods,omitempty" xml:"preferred_methods,omitempty" yaml:"preferred_methods,omitempty"` 55 DefaultMethod string `json:"default_method,omitempty" xml:"default_method,omitempty" yaml:"default_method,omitempty"` 56 Capable bool `json:"capable,omitempty" xml:"capable,omitempty" yaml:"capable,omitempty"` 57 injectKeyID bool 58 } 59 60 // CryptoKeyOperator represents CryptoKey operator. 61 type CryptoKeyOperator struct { 62 Token *CryptoKeyTokenOperator `json:"token,omitempty" xml:"token,omitempty" yaml:"token,omitempty"` 63 Secret interface{} `json:"secret,omitempty" xml:"secret,omitempty" yaml:"secret,omitempty"` 64 Capable bool `json:"capable,omitempty" xml:"capable,omitempty" yaml:"capable,omitempty"` 65 } 66 67 // NewCryptoKeyTokenOperator returns an instance of CryptoKeyTokenOperator. 68 func NewCryptoKeyTokenOperator() *CryptoKeyTokenOperator { 69 op := &CryptoKeyTokenOperator{} 70 op.Methods = make(map[string]interface{}) 71 return op 72 } 73 74 // NewCryptoKeyOperator returns an instance of CryptoKeyOperator. 75 func NewCryptoKeyOperator() *CryptoKeyOperator { 76 op := &CryptoKeyOperator{} 77 op.Token = NewCryptoKeyTokenOperator() 78 return op 79 } 80 81 func newCryptoKey() *CryptoKey { 82 k := &CryptoKey{} 83 k.Sign = NewCryptoKeyOperator() 84 k.Verify = NewCryptoKeyOperator() 85 return k 86 } 87 88 // GetKeysFromConfigs loads keys from one or more key configs. 89 func GetKeysFromConfigs(cfgs []*CryptoKeyConfig) ([]*CryptoKey, error) { 90 var keys []*CryptoKey 91 for _, cfg := range cfgs { 92 k, err := GetKeysFromConfig(cfg) 93 if err != nil { 94 return nil, err 95 } 96 keys = append(keys, k...) 97 } 98 return keys, nil 99 } 100 101 // GetKeysFromConfig loads keys from a single key config. 102 func GetKeysFromConfig(cfg *CryptoKeyConfig) ([]*CryptoKey, error) { 103 var keys []*CryptoKey 104 switch cfg.Source { 105 case "config": 106 switch { 107 case cfg.Algorithm == "hmac": 108 // Discovered shared key 109 k := newCryptoKey() 110 k.Config = cfg 111 keys = append(keys, k) 112 case cfg.FilePath != "": 113 fileKeys, err := extractKeysFromFile(cfg.FilePath, cfg) 114 if err != nil { 115 return nil, err 116 } 117 keys = append(keys, fileKeys...) 118 case cfg.DirPath != "": 119 dirKeys, err := extractKeysFromDir(cfg.DirPath, cfg) 120 if err != nil { 121 return nil, err 122 } 123 keys = append(keys, dirKeys...) 124 default: 125 return nil, fmt.Errorf("unsupported config") 126 } 127 case "env": 128 switch { 129 case cfg.EnvVarType == "key": 130 if strings.HasPrefix(cfg.EnvVarValue, "---") { 131 // Discovered symmetric key 132 k, err := extractKey([]byte(cfg.EnvVarValue), cfg) 133 if err != nil { 134 return nil, err 135 } 136 keys = append(keys, k) 137 break 138 } 139 // Discovered shared key 140 k := newCryptoKey() 141 k.Config = cfg 142 k.Config.Algorithm = "hmac" 143 k.Config.Secret = k.Config.EnvVarValue 144 keys = append(keys, k) 145 case cfg.EnvVarType == "file": 146 fileKeys, err := extractKeysFromFile(cfg.EnvVarValue, cfg) 147 if err != nil { 148 return nil, err 149 } 150 keys = append(keys, fileKeys...) 151 case cfg.EnvVarType == "directory": 152 dirKeys, err := extractKeysFromDir(cfg.EnvVarValue, cfg) 153 if err != nil { 154 return nil, err 155 } 156 keys = append(keys, dirKeys...) 157 default: 158 return nil, fmt.Errorf("unsupported env config type %s", cfg.EnvVarType) 159 } 160 case "generate": 161 switch cfg.Algorithm { 162 case "ecdsa": 163 cfg.parsed = true 164 key, err := generateKey(cfg, cfg.ID, "ES512") 165 if err != nil { 166 return nil, fmt.Errorf("generating key failed: %v", err) 167 } 168 keys = append(keys, key) 169 default: 170 return nil, fmt.Errorf("unsupported algorithm for generate: %s", cfg.Algorithm) 171 } 172 default: 173 return nil, fmt.Errorf("unsupported source: '%s'", cfg.Source) 174 } 175 176 for _, k := range keys { 177 switch k.Config.Algorithm { 178 case "hmac": 179 k.Sign.Capable = true 180 k.Verify.Capable = true 181 k.Sign.Secret = []byte(k.Config.Secret) 182 k.Verify.Secret = []byte(k.Config.Secret) 183 case "rsa", "ecdsa": 184 default: 185 return nil, fmt.Errorf("unsupported config algorithm %s", k.Config.Algorithm) 186 } 187 k.enableUsage() 188 } 189 return keys, nil 190 } 191 192 func (k *CryptoKey) enableUsage() { 193 methods := getMethodsPerAlgo(k.Config.Algorithm) 194 if k.Sign.Capable { 195 k.Sign.Token.ID = k.Config.ID 196 k.Sign.Token.Capable = true 197 if len(k.Sign.Token.PreferredMethods) == 0 { 198 k.Sign.Token.PreferredMethods = methods 199 } 200 for _, m := range k.Sign.Token.PreferredMethods { 201 k.Sign.Token.Methods[m] = true 202 } 203 k.Sign.Token.Name = k.Config.TokenName 204 k.Sign.Token.MaxLifetime = k.Config.TokenLifetime 205 k.Sign.Token.DefaultMethod = k.Sign.Token.PreferredMethods[0] 206 if k.Config.ID != defaultKeyID && k.Config.ID != "" { 207 k.Sign.Token.injectKeyID = true 208 } 209 } 210 if k.Verify.Capable { 211 k.Verify.Token.ID = k.Config.ID 212 k.Verify.Token.Capable = true 213 if len(k.Verify.Token.PreferredMethods) == 0 { 214 k.Verify.Token.PreferredMethods = methods 215 } 216 for _, m := range k.Verify.Token.PreferredMethods { 217 k.Verify.Token.Methods[m] = true 218 } 219 k.Verify.Token.Name = k.Config.TokenName 220 k.Verify.Token.MaxLifetime = k.Config.TokenLifetime 221 k.Verify.Token.DefaultMethod = k.Verify.Token.PreferredMethods[0] 222 } 223 } 224 225 // SignToken signs data using the requested method and returns it as string. 226 func (k *CryptoKey) SignToken(signMethod interface{}, usr *user.User) error { 227 if !k.Sign.Token.Capable { 228 return errors.ErrSigningKeyNotFound.WithArgs(signMethod) 229 } 230 response, err := k.sign(signMethod, usr.AsMap()) 231 if err != nil { 232 return err 233 } 234 usr.Token = response.(string) 235 return nil 236 } 237 238 func (k *CryptoKey) sign(signMethod, data interface{}) (interface{}, error) { 239 var method string 240 if signMethod == nil { 241 if k.Sign.Token.DefaultMethod == "" { 242 return nil, errors.ErrInvalidSigningMethod 243 } 244 method = k.Sign.Token.DefaultMethod 245 } else { 246 method = signMethod.(string) 247 if _, supported := k.Sign.Token.Methods[method]; !supported { 248 return nil, errors.ErrUnsupportedSigningMethod.WithArgs(method) 249 } 250 } 251 252 header := map[string]interface{}{"typ": "JWT", "alg": method} 253 if k.Sign.Token.injectKeyID { 254 header["kid"] = k.Sign.Token.ID 255 } 256 jh, err := json.Marshal(header) 257 if err != nil { 258 return nil, errors.ErrDataSigningFailed.WithArgs(method, err) 259 } 260 jb, err := json.Marshal(data) 261 if err != nil { 262 return nil, errors.ErrDataSigningFailed.WithArgs(method, err) 263 } 264 s := base64.RawURLEncoding.EncodeToString(jh) + "." + base64.RawURLEncoding.EncodeToString(jb) 265 266 switch signingMethods[method] { 267 case "hmac": 268 return k.signHMAC(method, s) 269 case "rsa": 270 return k.signRSA(method, s) 271 case "ecdsa": 272 return k.signECDSA(method, s) 273 } 274 275 return nil, errors.ErrDataSigningFailed.WithArgs(method, "unsupported method") 276 } 277 278 // ProvideKey returns the appropriate encryption key. 279 func (k *CryptoKey) ProvideKey(token *jwtlib.Token) (interface{}, error) { 280 switch k.Config.Algorithm { 281 case "hmac": 282 if _, validMethod := token.Method.(*jwtlib.SigningMethodHMAC); !validMethod { 283 return nil, errors.ErrUnexpectedSigningMethod.WithArgs("HS", token.Header["alg"]) 284 } 285 case "rsa": 286 if _, validMethod := token.Method.(*jwtlib.SigningMethodRSA); !validMethod { 287 return nil, errors.ErrUnexpectedSigningMethod.WithArgs("RS", token.Header["alg"]) 288 } 289 case "ecdsa": 290 if _, validMethod := token.Method.(*jwtlib.SigningMethodECDSA); !validMethod { 291 return nil, errors.ErrUnexpectedSigningMethod.WithArgs("ES", token.Header["alg"]) 292 } 293 } 294 return k.Verify.Secret, nil 295 } 296 297 func extractBytesFromFile(fp string) ([]byte, error) { 298 ext := filepath.Ext(fp) 299 switch ext { 300 case ".pem", ".key": 301 default: 302 return nil, errors.ErrCryptoKeyConfigFileNotSupported.WithArgs(fp) 303 } 304 b, err := ioutil.ReadFile(fp) 305 if err != nil { 306 return nil, errors.ErrCryptoKeyConfigReadFile.WithArgs(fp, err) 307 } 308 return b, nil 309 } 310 311 func extractKeysFromFile(fp string, cfg *CryptoKeyConfig) ([]*CryptoKey, error) { 312 var keys []*CryptoKey 313 b, err := extractBytesFromFile(fp) 314 if err != nil { 315 return nil, err 316 } 317 key, err := extractKey(b, cfg) 318 if err != nil { 319 return nil, errors.ErrCryptoKeyConfigReadFile.WithArgs(fp, err) 320 } 321 keys = append(keys, key) 322 if len(keys) == 0 { 323 return nil, errors.ErrCryptoKeyConfigFileKeyNotFound.WithArgs(fp) 324 } 325 return keys, nil 326 } 327 328 func extractKey(kb []byte, cfg *CryptoKeyConfig) (*CryptoKey, error) { 329 var curveName string 330 k := newCryptoKey() 331 kcfg := *cfg 332 k.Config = &kcfg 333 334 var block *pem.Block 335 if block, _ = pem.Decode(kb); block == nil { 336 return nil, errors.ErrNotPEMEncodedKey 337 } 338 339 switch { 340 case bytes.Contains(kb, []byte("RSA PRIVATE KEY")): 341 k.Config.Algorithm = "rsa" 342 privKey, err := x509.ParsePKCS1PrivateKey(block.Bytes) 343 if err != nil { 344 return nil, err 345 } 346 k.Sign.Capable = true 347 k.Sign.Secret = privKey 348 switch k.Config.Usage { 349 case "sign": 350 default: 351 k.Verify.Capable = true 352 k.Verify.Secret = privKey.Public() 353 } 354 case bytes.Contains(kb, []byte("EC PRIVATE KEY")): 355 k.Config.Algorithm = "ecdsa" 356 privKey, err := x509.ParseECPrivateKey(block.Bytes) 357 if err != nil { 358 return nil, err 359 } 360 k.Sign.Capable = true 361 k.Sign.Secret = privKey 362 curve := privKey.Curve.Params() 363 if curve == nil { 364 return nil, errors.ErrNoECDSACurveParamsFound 365 } 366 curveName = curve.Name 367 switch k.Config.Usage { 368 case "sign": 369 default: 370 k.Verify.Capable = true 371 k.Verify.Secret = privKey.Public() 372 } 373 case bytes.Contains(kb, []byte("PRIVATE KEY")): 374 privKey, err := x509.ParsePKCS8PrivateKey(block.Bytes) 375 if err != nil { 376 return nil, err 377 } 378 switch privKey := privKey.(type) { 379 case *rsa.PrivateKey: 380 k.Config.Algorithm = "rsa" 381 k.Sign.Capable = true 382 k.Sign.Secret = privKey 383 switch k.Config.Usage { 384 case "sign": 385 default: 386 k.Verify.Capable = true 387 k.Verify.Secret = privKey.Public() 388 } 389 case *ecdsa.PrivateKey: 390 k.Config.Algorithm = "ecdsa" 391 k.Sign.Capable = true 392 k.Sign.Secret = privKey 393 curve := privKey.Curve.Params() 394 if curve == nil { 395 return nil, errors.ErrNoECDSACurveParamsFound 396 } 397 curveName = curve.Name 398 switch k.Config.Usage { 399 case "sign": 400 default: 401 k.Verify.Capable = true 402 k.Verify.Secret = privKey.Public() 403 } 404 default: 405 // case ed25519.PrivateKey 406 return nil, errors.ErrCryptoKeyConfigUnsupportedPrivateKeyAlgo.WithArgs(privKey) 407 } 408 case bytes.Contains(kb, []byte("RSA PUBLIC KEY")): 409 pubKey, err := x509.ParsePKCS1PublicKey(block.Bytes) 410 if err != nil { 411 return nil, err 412 } 413 k.Config.Algorithm = "rsa" 414 k.Verify.Capable = true 415 k.Verify.Secret = pubKey 416 case bytes.Contains(kb, []byte("PUBLIC KEY")): 417 pubKey, err := x509.ParsePKIXPublicKey(block.Bytes) 418 if err != nil { 419 return nil, err 420 } 421 k.Verify.Capable = true 422 switch pubKey := pubKey.(type) { 423 case *rsa.PublicKey: 424 k.Config.Algorithm = "rsa" 425 k.Verify.Secret = pubKey 426 case *ecdsa.PublicKey: 427 k.Config.Algorithm = "ecdsa" 428 k.Verify.Secret = pubKey 429 curve := pubKey.Curve.Params() 430 if curve == nil { 431 return nil, errors.ErrNoECDSACurveParamsFound 432 } 433 curveName = curve.Name 434 default: 435 // case *dsa.PublicKey 436 // case ed25519.PublicKey 437 return nil, errors.ErrCryptoKeyConfigUnsupportedPublicKeyAlgo.WithArgs(pubKey) 438 } 439 default: 440 return nil, errors.ErrNotPEMEncodedKey 441 } 442 443 if k.Config.Algorithm == "ecdsa" { 444 // See https://golang.org/src/crypto/elliptic/elliptic.go. 445 var method string 446 switch curveName { 447 case "P-256": 448 method = "ES256" 449 case "P-384": 450 method = "ES384" 451 case "P-521": 452 method = "ES512" 453 default: 454 return nil, errors.ErrUnsupportedECDSACurve.WithArgs(curveName) 455 } 456 if k.Sign.Capable { 457 k.Sign.Token.PreferredMethods = []string{method} 458 } 459 if k.Verify.Capable { 460 k.Verify.Token.PreferredMethods = []string{method} 461 } 462 } 463 return k, nil 464 } 465 466 func extractKeysFromDir(dirPath string, cfg *CryptoKeyConfig) ([]*CryptoKey, error) { 467 var dirKeys []*CryptoKey 468 err := filepath.Walk(dirPath, func(fp string, fi os.FileInfo, err error) error { 469 if err != nil { 470 return err 471 } 472 if fi.IsDir() { 473 return nil 474 } 475 ext := filepath.Ext(fp) 476 switch ext { 477 case ".pem", ".key": 478 default: 479 return nil 480 } 481 482 kcfg := *cfg 483 ncfg := &kcfg 484 kid := filepath.Base(fp) 485 kid = strings.TrimSuffix(kid, ext) 486 kid = normalizeKeyID(kid) 487 488 ncfg.ID = kid 489 ncfg.FilePath = fp 490 491 keys, err := extractKeysFromFile(fp, ncfg) 492 if err != nil { 493 return err 494 } 495 dirKeys = append(dirKeys, keys...) 496 return nil 497 }) 498 if err != nil { 499 return nil, errors.ErrWalkDir.WithArgs(err) 500 } 501 if len(dirKeys) == 0 { 502 return nil, errors.ErrWalkDir.WithArgs("no crypto keys found") 503 } 504 return dirKeys, nil 505 } 506 507 func normalizeKeyID(s string) string { 508 b := []byte{} 509 for _, c := range []byte(s) { 510 if ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9') || c == '_' || c == '-' { 511 b = append(b, c) 512 } 513 } 514 return strings.ToLower(string(b)) 515 } 516 517 func (k *CryptoKey) signECDSA(method, data string) (interface{}, error) { 518 var h crypto.Hash 519 var cb int 520 switch method { 521 case "ES256": 522 h = crypto.SHA256 523 cb = 256 524 case "ES384": 525 h = crypto.SHA384 526 cb = 384 527 case "ES512": 528 h = crypto.SHA512 529 cb = 521 530 default: 531 return nil, errors.ErrDataSigningFailed.WithArgs("ECDSA", "unsupported method") 532 } 533 if !h.Available() { 534 return nil, errors.ErrDataSigningFailed.WithArgs("ECDSA", "unavailable method") 535 } 536 hf := h.New() 537 hf.Write([]byte(data)) 538 539 pk := k.Sign.Secret.(*ecdsa.PrivateKey) 540 if cb != pk.Curve.Params().BitSize { 541 return nil, errors.ErrDataSigningFailed.WithArgs("ECDSA", "curve bitsize mismatch") 542 } 543 544 r, s, err := ecdsa.Sign(rand.Reader, pk, hf.Sum(nil)) 545 if err != nil { 546 return nil, errors.ErrDataSigningFailed.WithArgs("ECDSA", err) 547 } 548 549 sz := cb / 8 550 if cb%8 > 0 { 551 sz++ 552 } 553 554 b := make([]byte, 2*sz) 555 r.FillBytes(b[0:sz]) 556 s.FillBytes(b[sz:]) 557 return data + "." + base64.RawURLEncoding.EncodeToString(b), nil 558 } 559 560 func (k *CryptoKey) signRSA(method, data string) (interface{}, error) { 561 var h crypto.Hash 562 switch method { 563 case "RS256": 564 h = crypto.SHA256 565 case "RS384": 566 h = crypto.SHA384 567 case "RS512": 568 h = crypto.SHA512 569 default: 570 return nil, errors.ErrDataSigningFailed.WithArgs("RSA", "unsupported method") 571 } 572 if !h.Available() { 573 return nil, errors.ErrDataSigningFailed.WithArgs("RSA", "unavailable method") 574 } 575 hf := h.New() 576 hf.Write([]byte(data)) 577 578 pk := k.Sign.Secret.(*rsa.PrivateKey) 579 b, err := rsa.SignPKCS1v15(rand.Reader, pk, h, hf.Sum(nil)) 580 if err != nil { 581 return nil, errors.ErrDataSigningFailed.WithArgs("RSA", err) 582 } 583 return data + "." + base64.RawURLEncoding.EncodeToString(b), nil 584 } 585 586 func (k *CryptoKey) signHMAC(method, data string) (interface{}, error) { 587 var h crypto.Hash 588 switch method { 589 case "HS256": 590 h = crypto.SHA256 591 case "HS384": 592 h = crypto.SHA384 593 case "HS512": 594 h = crypto.SHA512 595 default: 596 return nil, errors.ErrDataSigningFailed.WithArgs("HMAC", "unsupported method") 597 } 598 if !h.Available() { 599 return nil, errors.ErrDataSigningFailed.WithArgs("HMAC", "unavailable method") 600 } 601 pk := k.Sign.Secret.([]byte) 602 hf := hmac.New(h.New, pk) 603 hf.Write([]byte(data)) 604 return data + "." + base64.RawURLEncoding.EncodeToString(hf.Sum(nil)), nil 605 } 606 607 func generateKey(cfg *CryptoKeyConfig, tag, algo string) (*CryptoKey, error) { 608 generateES512Key := func() ([]byte, error) { 609 c := elliptic.P521() 610 priv, err := ecdsa.GenerateKey(c, rand.Reader) 611 if err != nil { 612 return nil, err 613 } 614 if !c.IsOnCurve(priv.PublicKey.X, priv.PublicKey.Y) { 615 return nil, err 616 } 617 derBytes, err := x509.MarshalECPrivateKey(priv) 618 if err != nil { 619 return nil, err 620 } 621 pemBytes := pem.EncodeToMemory( 622 &pem.Block{ 623 Type: "EC PRIVATE KEY", 624 Bytes: derBytes, 625 }, 626 ) 627 return pemBytes, nil 628 } 629 var ( 630 generateKey func() ([]byte, error) 631 generated bool 632 kb string 633 ) 634 switch algo { 635 case "ES512": 636 generateKey = generateES512Key 637 default: 638 return nil, errors.ErrCryptoKeyStoreAutoGenerateAlgo.WithArgs(algo) 639 } 640 for i := 1; i < 5; i++ { 641 pemBytes, err := generateKey() 642 if err != nil || pemBytes == nil { 643 // try again 644 continue 645 } 646 kb = string(pemBytes) 647 generated = true 648 } 649 650 if !generated { 651 return nil, errors.ErrCryptoKeyStoreAutoGenerateFailed.WithArgs("failed") 652 } 653 if err := shared.Buffer.Add(tag, kb); err != nil { 654 if err.Error() != "not empty" { 655 return nil, errors.ErrCryptoKeyStoreAutoGenerateFailed.WithArgs(err) 656 } 657 kb, err = shared.Buffer.Get(tag) 658 if err != nil { 659 return nil, errors.ErrCryptoKeyStoreAutoGenerateFailed.WithArgs(err) 660 } 661 } 662 key, err := extractKey([]byte(kb), cfg) 663 if err != nil { 664 return nil, errors.ErrCryptoKeyStoreAutoGenerateFailed.WithArgs(err) 665 } 666 667 return key, nil 668 }