diff --git a/CHANGELOG.md b/CHANGELOG.md index 10ba8dac..30eb32cb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +### Added + +AWS KMS support for account keys. Accounts can now be configured with the `aws-kms` key type, which signs transactions using an AWS KMS asymmetric key referenced by its ARN. It works like the existing `google-kms` key type, only the `resourceID` format differs: + +```json +"accounts": { + "my-account": { + "address": "0x0000000000000000", + "key": { + "type": "aws-kms", + "resourceID": "arn:aws:kms:us-west-2:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab" + } + } +} +``` + +The region is read from the ARN, credentials are resolved from the default AWS configuration, and the key must be an ECDSA P-256 or secp256k1 key. AWS KMS signs those keys with `ECDSA_SHA_256` only, so the hash algorithm is fixed to `SHA2_256` and setting a different one in the configuration is an error. + ## 1.0.0 ### Changed diff --git a/accounts/account_test.go b/accounts/account_test.go index 07419286..3da051c0 100644 --- a/accounts/account_test.go +++ b/accounts/account_test.go @@ -22,7 +22,10 @@ import ( "testing" "github.com/onflow/flow-go-sdk" + "github.com/onflow/flow-go-sdk/crypto" "github.com/stretchr/testify/assert" + + "github.com/onflow/flowkit/v2/config" ) func Test_Accounts(t *testing.T) { @@ -128,3 +131,35 @@ func Test_Accounts(t *testing.T) { }) } + +func Test_AWSKMS_AccountRoundTrip(t *testing.T) { + conf := config.Config{ + Accounts: config.Accounts{ + { + Name: "aws-kms-account", + Address: flow.HexToAddress("0xf8d6e0586b0a20c7"), + Key: config.AccountKey{ + Type: config.KeyTypeAWSKMS, + SigAlgo: config.DefaultSigAlgo, + HashAlgo: config.DefaultHashAlgo, // must be replaced by SHA2_256 + ResourceID: "arn:aws:kms:us-west-2:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab", + }, + }, + }, + } + + accounts, err := FromConfig(&conf) + assert.NoError(t, err) + + account, err := accounts.ByName("aws-kms-account") + assert.NoError(t, err) + assert.Equal(t, config.KeyTypeAWSKMS, account.Key.Type()) + assert.Equal(t, crypto.SHA2_256, account.Key.HashAlgo()) + + roundTripped := ToConfig(accounts) + assert.Equal(t, crypto.SHA2_256, roundTripped[0].Key.HashAlgo) + assert.Equal(t, + "arn:aws:kms:us-west-2:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab", + roundTripped[0].Key.ResourceID, + ) +} diff --git a/accounts/keys.go b/accounts/keys.go index 79dd0e69..c27632b6 100644 --- a/accounts/keys.go +++ b/accounts/keys.go @@ -27,9 +27,12 @@ import ( "regexp" "strings" + "github.com/aws/aws-sdk-go-v2/aws" + awsconfig "github.com/aws/aws-sdk-go-v2/config" gethAccounts "github.com/ethereum/go-ethereum/accounts" "github.com/lmars/go-slip10" "github.com/onflow/flow-go-sdk/crypto" + "github.com/onflow/flow-go-sdk/crypto/awskms" "github.com/onflow/flow-go-sdk/crypto/cloudkms" "github.com/tyler-smith/go-bip39" @@ -69,6 +72,8 @@ var _ Key = &HexKey{} var _ Key = &KMSKey{} +var _ Key = &AWSKMSKey{} + var _ Key = &BIP44Key{} var _ Key = &EnvKey{} @@ -84,6 +89,8 @@ func keyFromConfig(accountKeyConf config.AccountKey) (Key, error) { return bip44KeyFromConfig(accountKeyConf) case config.KeyTypeGoogleKMS: return kmsKeyFromConfig(accountKeyConf) + case config.KeyTypeAWSKMS: + return awsKMSKeyFromConfig(accountKeyConf) case config.KeyTypeFile: return fileKeyFromConfig(accountKeyConf) } @@ -228,6 +235,106 @@ func kmsKeyFromConfig(key config.AccountKey) (Key, error) { }, nil } +// AWSKMSKey implements AWS KMS system for signing. +type AWSKMSKey struct { + *baseKey + kmsKey awskms.Key +} + +// ToConfig convert account key to configuration. +func (a *AWSKMSKey) ToConfig() config.AccountKey { + return config.AccountKey{ + Type: a.keyType, + Index: a.index, + SigAlgo: a.sigAlgo, + HashAlgo: a.hashAlgo, + ResourceID: a.kmsKey.ARN(), + } +} + +func (a *AWSKMSKey) Signer(ctx context.Context) (crypto.Signer, error) { + awsCfg, err := awsKMSConfig(ctx, a.kmsKey) + if err != nil { + return nil, err + } + + accountKMSSigner, err := awskms.NewClient(awsCfg).SignerForKey( + ctx, + a.kmsKey, + ) + if err != nil { + return nil, err + } + + return accountKMSSigner, nil +} + +func (a *AWSKMSKey) Validate() error { + awsCfg, err := awsKMSConfig(context.Background(), a.kmsKey) + if err != nil { + return err + } + + if _, err = awsCfg.Credentials.Retrieve(context.Background()); err != nil { + return fmt.Errorf( + "could not load AWS credentials for KMS key %s, configure them with `aws configure sso` or `aws sso login`: %w", + a.kmsKey.ARN(), + err, + ) + } + + return nil +} + +func (a *AWSKMSKey) PrivateKey() (*crypto.PrivateKey, error) { + return nil, fmt.Errorf("private key not accessible") +} + +// awsKMSKeyARNPattern matches the canonical AWS KMS key ARN, for example +// arn:aws:kms:us-west-2:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab +// +// Key aliases and other KMS resource types are not supported because they cannot be +// reconstructed from a parsed key, and the signing key must be addressed by its ARN. +// +// Only the standard `aws` partition is accepted. awskms.Key.ARN() rebuilds the ARN as +// arn:aws:kms:::key/ and the SDK sends that as the KeyId, so a +// aws-cn or aws-us-gov key would be addressed with the wrong partition and rejected by +// the service. Supporting those partitions needs a change in the SDK. +var awsKMSKeyARNPattern = regexp.MustCompile(`^arn:aws:kms:[a-z0-9-]+:[0-9]{12}:key/[A-Za-z0-9-]+$`) + +func awsKMSKeyFromConfig(key config.AccountKey) (Key, error) { + if !awsKMSKeyARNPattern.MatchString(key.ResourceID) { + return nil, fmt.Errorf( + "invalid AWS KMS resource ID %q, expected the ARN format arn:aws:kms:::key/ (the aws-cn and aws-us-gov partitions are not supported)", + key.ResourceID, + ) + } + + accountKMSKey, err := awskms.KeyFromResourceARN(key.ResourceID) + if err != nil { + return nil, err + } + + return &AWSKMSKey{ + baseKey: &baseKey{ + keyType: config.KeyTypeAWSKMS, + index: key.Index, + sigAlgo: key.SigAlgo, + // AWS KMS signs ECDSA keys with ECDSA_SHA_256 only, so the configured hash + // algorithm is overridden rather than trusted. The JSON layer rejects a + // conflicting value; this covers programmatic construction of the config. + hashAlgo: crypto.SHA2_256, + }, + kmsKey: accountKMSKey, + }, nil +} + +// awsKMSConfig loads the default AWS configuration, defaulting the region to the one +// the KMS key lives in so a region does not have to be configured separately. +func awsKMSConfig(ctx context.Context, key awskms.Key) (aws.Config, error) { + return awsconfig.LoadDefaultConfig(ctx, awsconfig.WithRegion(key.Region)) +} + // HexKey implements account key in hex representation. type HexKey struct { *baseKey diff --git a/accounts/keys_test.go b/accounts/keys_test.go index 2713e524..d92a8173 100644 --- a/accounts/keys_test.go +++ b/accounts/keys_test.go @@ -47,6 +47,66 @@ func Test_KMS_Keys(t *testing.T) { assert.Equal(t, confKey, kmsKey.ToConfig()) } +func Test_AWSKMS_Keys(t *testing.T) { + confKey := config.AccountKey{ + Type: config.KeyTypeAWSKMS, + Index: 0, + SigAlgo: config.DefaultSigAlgo, + HashAlgo: crypto.SHA2_256, + ResourceID: "arn:aws:kms:us-west-2:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab", + } + + kmsKey, err := keyFromConfig(confKey) + assert.NoError(t, err) + + _, err = kmsKey.PrivateKey() + assert.EqualError(t, err, "private key not accessible") + assert.Equal(t, confKey, kmsKey.ToConfig()) +} + +// AWS KMS signs ECDSA keys with ECDSA_SHA_256 only, so the configured hash algorithm must +// never reach the signer. +func Test_AWSKMS_KeysHashAlgorithm(t *testing.T) { + confKey := config.AccountKey{ + Type: config.KeyTypeAWSKMS, + SigAlgo: config.DefaultSigAlgo, + HashAlgo: crypto.SHA3_256, + ResourceID: "arn:aws:kms:us-west-2:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab", + } + + kmsKey, err := keyFromConfig(confKey) + assert.NoError(t, err) + + assert.Equal(t, crypto.SHA2_256, kmsKey.HashAlgo()) + assert.Equal(t, crypto.SHA2_256, kmsKey.ToConfig().HashAlgo) +} + +func Test_AWSKMS_KeysInvalidResourceID(t *testing.T) { + tests := map[string]string{ + "empty": "", + "google resource": "projects/my-project/locations/global/keyRings/flow/cryptoKeys/my-account/cryptoKeyVersions/1", + "key without id": "arn:aws:kms:us-west-2:111122223333:key", + "alias": "arn:aws:kms:us-west-2:111122223333:alias/my-key", + "short account id": "arn:aws:kms:us-west-2:1111222233333:key/1234abcd", + "missing region": "arn:aws:kms::111122223333:key/1234abcd", + "missing account": "arn:aws:kms:us-west-2::key/1234abcd", + "govcloud": "arn:aws-us-gov:kms:us-gov-west-1:111122223333:key/1234abcd", + "china": "arn:aws-cn:kms:cn-north-1:111122223333:key/1234abcd", + } + + for name, resourceID := range tests { + t.Run(name, func(t *testing.T) { + _, err := keyFromConfig(config.AccountKey{ + Type: config.KeyTypeAWSKMS, + SigAlgo: config.DefaultSigAlgo, + HashAlgo: config.DefaultHashAlgo, + ResourceID: resourceID, + }) + assert.Error(t, err) + }) + } +} + func Test_File_key(t *testing.T) { confKey := config.AccountKey{ Type: config.KeyTypeFile, diff --git a/config/config.go b/config/config.go index ee5b35ca..b9802448 100644 --- a/config/config.go +++ b/config/config.go @@ -46,6 +46,7 @@ type KeyType string const ( KeyTypeHex KeyType = "hex" KeyTypeGoogleKMS KeyType = "google-kms" + KeyTypeAWSKMS KeyType = "aws-kms" KeyTypeBip44 KeyType = "bip44" KeyTypeFile KeyType = "file" ) diff --git a/config/json/account.go b/config/json/account.go index ec454c33..ebaa6744 100644 --- a/config/json/account.go +++ b/config/json/account.go @@ -154,11 +154,20 @@ func transformAdvancedToConfig(accountName string, a advancedAccount) (*config.A return nil, fmt.Errorf("invalid hash algorithm for account %s", accountName) } - validTypes := []config.KeyType{config.KeyTypeHex, config.KeyTypeFile, config.KeyTypeBip44, config.KeyTypeGoogleKMS} + validTypes := []config.KeyType{config.KeyTypeHex, config.KeyTypeFile, config.KeyTypeBip44, config.KeyTypeGoogleKMS, config.KeyTypeAWSKMS} if !slices.Contains(validTypes, a.Key.Type) { return nil, fmt.Errorf("invalid key type for account %s", accountName) } + // AWS KMS signs ECDSA keys with ECDSA_SHA_256 only, so the hash algorithm is not a + // choice for that key type. + if a.Key.Type == config.KeyTypeAWSKMS { + if a.Key.HashAlgo != "" && hashAlgo != crypto.SHA2_256 { + return nil, fmt.Errorf("invalid hash algorithm for account %s: aws-kms keys only support SHA2_256", accountName) + } + hashAlgo = crypto.SHA2_256 + } + // check that only one is provided because the values are mutually exclusive set := false for _, v := range []string{a.Key.ResourceID, a.Key.PrivateKey, a.Key.Location} { @@ -217,7 +226,7 @@ func transformAdvancedToConfig(accountName string, a advancedAccount) (*config.A key.DerivationPath = "m/44'/539'/0'/0/0" } - case config.KeyTypeGoogleKMS: + case config.KeyTypeGoogleKMS, config.KeyTypeAWSKMS: if a.Key.ResourceID == "" { return nil, fmt.Errorf("missing resource ID value for key on account %s", accountName) } @@ -323,7 +332,7 @@ func transformAdvancedKeyToJSON(key config.AccountKey) advanceKey { case config.KeyTypeBip44: advancedKey.Mnemonic = key.Mnemonic advancedKey.DerivationPath = key.DerivationPath - case config.KeyTypeGoogleKMS: + case config.KeyTypeGoogleKMS, config.KeyTypeAWSKMS: advancedKey.ResourceID = key.ResourceID case config.KeyTypeFile: advancedKey.Location = filepath.ToSlash(key.Location) @@ -357,7 +366,7 @@ type advanceKey struct { // bip44 key type Mnemonic string `json:"mnemonic,omitempty"` DerivationPath string `json:"derivationPath,omitempty"` - // kms key type + // kms key types (google-kms, aws-kms) ResourceID string `json:"resourceID,omitempty"` // key location Location string `json:"location,omitempty"` diff --git a/config/json/account_test.go b/config/json/account_test.go index b4fdaaa5..77d30ef1 100644 --- a/config/json/account_test.go +++ b/config/json/account_test.go @@ -158,6 +158,67 @@ func Test_ConfigAccountKeysAdvancedKMS(t *testing.T) { assert.Nil(t, key.PrivateKey) } +func Test_ConfigAccountKeysAdvancedAWSKMS(t *testing.T) { + b := []byte(`{ + "test": { + "address": "service", + "key": { + "type": "aws-kms", + "index": 1, + "signatureAlgorithm": "ECDSA_P256", + "resourceID": "arn:aws:kms:us-west-2:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab" + } + } + }`) + + var jsonAccounts jsonAccounts + err := json.Unmarshal(b, &jsonAccounts) + assert.NoError(t, err) + + accounts, err := jsonAccounts.transformToConfig() + assert.NoError(t, err) + + account, err := accounts.ByName("test") + assert.NoError(t, err) + key := account.Key + + assert.Equal(t, "f8d6e0586b0a20c7", account.Address.String()) + assert.Equal(t, "SHA2_256", key.HashAlgo.String()) + assert.Equal(t, uint32(1), key.Index) + assert.Equal(t, "ECDSA_P256", key.SigAlgo.String()) + assert.Equal(t, "arn:aws:kms:us-west-2:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab", key.ResourceID) + assert.Nil(t, key.PrivateKey) + + jsonAccs := transformAccountsToJSON(accounts) + result, err := json.Marshal(jsonAccs) + assert.NoError(t, err) + + assert.JSONEq(t, + `{"test":{"address":"f8d6e0586b0a20c7","key":{"type":"aws-kms","index":1,"hashAlgorithm":"SHA2_256","resourceID":"arn:aws:kms:us-west-2:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab"}}}`, + string(result), + ) +} + +func Test_ConfigAccountKeysAdvancedAWSKMSInvalidHashAlgorithm(t *testing.T) { + b := []byte(`{ + "test": { + "address": "service", + "key": { + "type": "aws-kms", + "hashAlgorithm": "SHA3_256", + "resourceID": "arn:aws:kms:us-west-2:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab" + } + } + }`) + + var jsonAccounts jsonAccounts + err := json.Unmarshal(b, &jsonAccounts) + assert.NoError(t, err) + + _, err = jsonAccounts.transformToConfig() + assert.EqualError(t, err, "invalid hash algorithm for account test: aws-kms keys only support SHA2_256") +} + func Test_ConfigAccountOldFormats(t *testing.T) { b := []byte(`{ "old-format-1": { diff --git a/go.mod b/go.mod index e126921b..70a28004 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,8 @@ module github.com/onflow/flowkit/v2 go 1.26.0 require ( + github.com/aws/aws-sdk-go-v2 v1.41.1 + github.com/aws/aws-sdk-go-v2/config v1.32.7 github.com/btcsuite/btcd/chaincfg/chainhash v1.0.2 github.com/ethereum/go-ethereum v1.17.4 github.com/gosuri/uilive v0.0.4 @@ -41,6 +43,19 @@ require ( github.com/SaveTheRbtz/mph v0.1.1-0.20240117162131-4166ec7869bc // indirect github.com/StackExchange/wmi v1.2.1 // indirect github.com/VictoriaMetrics/fastcache v1.13.0 // indirect + github.com/aws/aws-sdk-go-v2/credentials v1.19.7 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.17 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.17 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.17 // indirect + github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.4 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.17 // indirect + github.com/aws/aws-sdk-go-v2/service/kms v1.50.0 // indirect + github.com/aws/aws-sdk-go-v2/service/signin v1.0.5 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.30.9 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.13 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.41.6 // indirect + github.com/aws/smithy-go v1.24.0 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bits-and-blooms/bitset v1.24.4 // indirect github.com/cenkalti/backoff/v5 v5.0.3 // indirect diff --git a/go.sum b/go.sum index 6e295a08..ef1c3686 100644 --- a/go.sum +++ b/go.sum @@ -54,16 +54,38 @@ github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/aws/aws-sdk-go-v2 v1.41.1 h1:ABlyEARCDLN034NhxlRUSZr4l71mh+T5KAeGh6cerhU= github.com/aws/aws-sdk-go-v2 v1.41.1/go.mod h1:MayyLB8y+buD9hZqkCW3kX1AKq07Y5pXxtgB+rRFhz0= +github.com/aws/aws-sdk-go-v2/config v1.32.7 h1:vxUyWGUwmkQ2g19n7JY/9YL8MfAIl7bTesIUykECXmY= +github.com/aws/aws-sdk-go-v2/config v1.32.7/go.mod h1:2/Qm5vKUU/r7Y+zUk/Ptt2MDAEKAfUtKc1+3U1Mo3oY= +github.com/aws/aws-sdk-go-v2/credentials v1.19.7 h1:tHK47VqqtJxOymRrNtUXN5SP/zUTvZKeLx4tH6PGQc8= +github.com/aws/aws-sdk-go-v2/credentials v1.19.7/go.mod h1:qOZk8sPDrxhf+4Wf4oT2urYJrYt3RejHSzgAquYeppw= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.17 h1:I0GyV8wiYrP8XpA70g1HBcQO1JlQxCMTW9npl5UbDHY= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.17/go.mod h1:tyw7BOl5bBe/oqvoIeECFJjMdzXoa/dfVz3QQ5lgHGA= github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.5.1 h1:VGkV9KmhGqOQWnHyi4gLG98kE6OecT42fdrCGFWxJsc= github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.5.1/go.mod h1:PLlnMiki//sGnCJiW+aVpvP/C8Kcm8mEj/IVm9+9qk4= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.17 h1:xOLELNKGp2vsiteLsvLPwxC+mYmO6OZ8PYgiuPJzF8U= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.17/go.mod h1:5M5CI3D12dNOtH3/mk6minaRwI2/37ifCURZISxA/IQ= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.17 h1:WWLqlh79iO48yLkj1v3ISRNiv+3KdQoZ6JWyfcsyQik= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.17/go.mod h1:EhG22vHRrvF8oXSTYStZhJc1aUgKtnJe+aOiFEV90cM= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 h1:WKuaxf++XKWlHWu9ECbMlha8WOEGm0OUEZqm4K/Gcfk= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4/go.mod h1:ZWy7j6v1vWGmPReu0iSGvRiise4YI5SkR3OHKTZ6Wuc= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.4 h1:0ryTNEdJbzUCEWkVXEXoqlXV72J5keC1GvILMOuD00E= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.4/go.mod h1:HQ4qwNZh32C3CBeO6iJLQlgtMzqeG17ziAA/3KDJFow= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.17 h1:RuNSMoozM8oXlgLG/n6WLaFGoea7/CddrCfIiSA+xdY= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.17/go.mod h1:F2xxQ9TZz5gDWsclCtPQscGpP0VUOc8RqgFM3vDENmU= github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.7.0 h1:HWsM0YQWX76V6MOp07YuTYacm8k7h69ObJuw7Nck+og= github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.7.0/go.mod h1:LKb3cKNQIMh+itGnEpKGcnL/6OIjPZqrtYah1w5f+3o= +github.com/aws/aws-sdk-go-v2/service/kms v1.50.0 h1:XSvRJBoDObL6Sn4cRmvH9wqjxjL7wf1ZDolUEyP7hw4= +github.com/aws/aws-sdk-go-v2/service/kms v1.50.0/go.mod h1:1SdcmEGUEQE1mrU2sIgeHtcMSxHuybhPvuEPANzIDfI= github.com/aws/aws-sdk-go-v2/service/s3 v1.15.0 h1:nPLfLPfglacc29Y949sDxpr3X/blaY40s3B85WT2yZU= github.com/aws/aws-sdk-go-v2/service/s3 v1.15.0/go.mod h1:Iv2aJVtVSm/D22rFoX99cLG4q4uB7tppuCsulGe98k4= +github.com/aws/aws-sdk-go-v2/service/signin v1.0.5 h1:VrhDvQib/i0lxvr3zqlUwLwJP4fpmpyD9wYG1vfSu+Y= +github.com/aws/aws-sdk-go-v2/service/signin v1.0.5/go.mod h1:k029+U8SY30/3/ras4G/Fnv/b88N4mAfliNn08Dem4M= +github.com/aws/aws-sdk-go-v2/service/sso v1.30.9 h1:v6EiMvhEYBoHABfbGB4alOYmCIrcgyPPiBE1wZAEbqk= +github.com/aws/aws-sdk-go-v2/service/sso v1.30.9/go.mod h1:yifAsgBxgJWn3ggx70A3urX2AN49Y5sJTD1UQFlfqBw= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.13 h1:gd84Omyu9JLriJVCbGApcLzVR3XtmC4ZDPcAI6Ftvds= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.13/go.mod h1:sTGThjphYE4Ohw8vJiRStAcu3rbjtXRsdNB0TvZ5wwo= +github.com/aws/aws-sdk-go-v2/service/sts v1.41.6 h1:5fFjR/ToSOzB2OQ/XqWpZBmNvmP/pJ1jOWYlFDJTjRQ= +github.com/aws/aws-sdk-go-v2/service/sts v1.41.6/go.mod h1:qgFDZQSD/Kys7nJnVqYlWKnh0SSdMjAi0uSwON4wgYQ= github.com/aws/smithy-go v1.24.0 h1:LpilSUItNPFr1eY85RYgTIg5eIEPtvFbskaFcmmIUnk= github.com/aws/smithy-go v1.24.0/go.mod h1:LEj2LM3rBRQJxPZTB4KuzZkaZYnZPnvgIhb4pu07mx0= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=