diff --git a/age/keysource.go b/age/keysource.go index 83bdbe0a6..92b431e0c 100644 --- a/age/keysource.go +++ b/age/keysource.go @@ -1,6 +1,7 @@ package age import ( + "bufio" "bytes" "errors" "fmt" @@ -13,7 +14,9 @@ import ( "filippo.io/age" "filippo.io/age/armor" "github.com/sirupsen/logrus" + "golang.org/x/term" + gpgagent "github.com/getsops/gopgagent" "github.com/getsops/sops/v3/logging" ) @@ -284,11 +287,105 @@ func (key *MasterKey) loadIdentities() (ParsedIdentities, error) { var identities ParsedIdentities for n, r := range readers { - ids, err := age.ParseIdentities(r) - if err != nil { - return nil, fmt.Errorf("failed to parse '%s' age identities: %w", n, err) + + b := bufio.NewReader(r) + p, _ := b.Peek(14) // length of "age-encryption" and "-----BEGIN AGE" + peeked := string(p) + + switch { + // An age encrypted file, plain or armored. + case peeked == "age-encryption" || peeked == "-----BEGIN AGE": + var r io.Reader = b + if peeked == "-----BEGIN AGE" { + r = armor.NewReader(r) + } + const privateKeySizeLimit = 1 << 24 // 16 MiB + contents, err := io.ReadAll(io.LimitReader(r, privateKeySizeLimit)) + if err != nil { + return nil, fmt.Errorf("failed to read '%s': %w", n, err) + } + if len(contents) == privateKeySizeLimit { + return nil, fmt.Errorf("failed to read '%s': file too long", n) + } + IncorrectPassphrase := func() { + conn, err := gpgagent.NewConn() + if err != nil { + return + } + defer func(conn *gpgagent.Conn) { + if err := conn.Close(); err != nil { + log.Errorf("failed to close connection with gpg-agent: %s", err) + } + }(conn) + err = conn.RemoveFromCache(n) + if err != nil { + log.Warnf("gpg-agent remove cache request errored: %s", err) + return + } + } + ids := []age.Identity{&EncryptedIdentity{ + Contents: contents, + Passphrase: func() (string, error) { + conn, err := gpgagent.NewConn() + if err != nil { + fmt.Fprintf(os.Stderr, "Enter passphrase for identity '%s':", n) + + var pass string + if term.IsTerminal(int(os.Stdout.Fd())) { + p, err = term.ReadPassword(int(os.Stdout.Fd())) + if err == nil { + pass = string(p) + } + } else { + reader := bufio.NewReader(os.Stdin) + pass, err = reader.ReadString('\n') + if err == io.EOF { + err = nil + } + } + if err != nil { + return "", fmt.Errorf("could not read passphrase: %v", err) + } + + fmt.Fprintln(os.Stderr) + + return pass, nil + } + defer func(conn *gpgagent.Conn) { + if err := conn.Close(); err != nil { + log.Errorf("failed to close connection with gpg-agent: %s", err) + } + }(conn) + + req := gpgagent.PassphraseRequest{ + // TODO is the cachekey good enough? + CacheKey: n, + Prompt: "Passphrase", + Desc: fmt.Sprintf("Enter passphrase for identity '%s':", n), + } + pass, err := conn.GetPassphrase(&req) + if err != nil { + return "", fmt.Errorf("gpg-agent passphrase request errored: %s", err) + } + //make sure that we won't store empty pass + if len(pass) == 0 { + IncorrectPassphrase() + } + return pass, nil + }, + IncorrectPassphrase: IncorrectPassphrase, + NoMatchWarning: func() { + log.Warnf("encrypted identity '%s' didn't match file's recipients", n) + }, + }} + identities = append(identities, ids...) + default: + ids, err := age.ParseIdentities(b) + if err != nil { + return nil, fmt.Errorf("failed to parse '%s' age identities: %w", n, err) + } + identities = append(identities, ids...) } - identities = append(identities, ids...) } return identities, nil } @@ -317,3 +414,83 @@ func parseIdentities(identity ...string) (ParsedIdentities, error) { } return identities, nil } + +type EncryptedIdentity struct { + Contents []byte + Passphrase func() (string, error) + NoMatchWarning func() + IncorrectPassphrase func() + + identities []age.Identity +} + +var _ age.Identity = &EncryptedIdentity{} + +func (i *EncryptedIdentity) Unwrap(stanzas []*age.Stanza) (fileKey []byte, err error) { + if i.identities == nil { + if err := i.decrypt(); err != nil { + return nil, err + } + } + + for _, id := range i.identities { + fileKey, err = id.Unwrap(stanzas) + if errors.Is(err, age.ErrIncorrectIdentity) { + continue + } + if err != nil { + return nil, err + } + return fileKey, nil + } + i.NoMatchWarning() + return nil, age.ErrIncorrectIdentity +} + +func (i *EncryptedIdentity) decrypt() error { + d, err := age.Decrypt(bytes.NewReader(i.Contents), &LazyScryptIdentity{i.Passphrase}) + if e := new(age.NoIdentityMatchError); errors.As(err, &e) { + // ScryptIdentity returns ErrIncorrectIdentity for an incorrect + // passphrase, which would lead Decrypt to returning "no identity + // matched any recipient". That makes sense in the API, where there + // might be multiple configured ScryptIdentity. Since in cmd/age there + // can be only one, return a better error message. + i.IncorrectPassphrase() + return fmt.Errorf("incorrect passphrase") + } + if err != nil { + return fmt.Errorf("failed to decrypt identity file: %v", err) + } + i.identities, err = age.ParseIdentities(d) + return err +} + +// LazyScryptIdentity is an age.Identity that requests a passphrase only if it +// encounters an scrypt stanza. After obtaining a passphrase, it delegates to +// ScryptIdentity. +type LazyScryptIdentity struct { + Passphrase func() (string, error) +} + +var _ age.Identity = &LazyScryptIdentity{} + +func (i *LazyScryptIdentity) Unwrap(stanzas []*age.Stanza) (fileKey []byte, err error) { + for _, s := range stanzas { + if s.Type == "scrypt" && len(stanzas) != 1 { + return nil, errors.New("an scrypt recipient must be the only one") + } + } + if len(stanzas) != 1 || stanzas[0].Type != "scrypt" { + return nil, age.ErrIncorrectIdentity + } + pass, err := i.Passphrase() + if err != nil { + return nil, fmt.Errorf("could not read passphrase: %v", err) + } + ii, err := age.NewScryptIdentity(pass) + if err != nil { + return nil, err + } + fileKey, err = ii.Unwrap(stanzas) + return fileKey, err +} diff --git a/age/keysource_test.go b/age/keysource_test.go index 1a07058a6..b99a95415 100644 --- a/age/keysource_test.go +++ b/age/keysource_test.go @@ -2,6 +2,7 @@ package age import ( "fmt" + "io/ioutil" "os" "path/filepath" "runtime" @@ -28,6 +29,18 @@ EylloI7MNGbadPGb -----END AGE ENCRYPTED FILE-----` // mockEncryptedKeyPlain is the plain value of mockEncryptedKey. mockEncryptedKeyPlain string = "data" + // passphrase used to encrypt age identity. + mockIdentityPassphrase string = "passphrase" + mockEncryptedIdentity string = `-----BEGIN AGE ENCRYPTED FILE----- +YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNjcnlwdCBMN2FXZW9xSFViYjdNeW5D +dy9iSHFnIDE4Ck9zV0ZoNldmci9rL3VXd3BtZmQvK3VZWEpBQjdhZ0UrcmhqR2lF +YThFMzAKLS0tIGVEQ0xwODI1TlNYeHNHaHZKWHoyLzYwMTMvTGhaZG1oa203cSs0 +VUpBL1kKsaTnt+H/z8mkL21UYKIt3YMpWSV/oYqTm1cSSUnF9InZEYU9HndK9rc8 +ni+MTJCmYf4mgvvGPMf7oIQvs6ijaTdlQb+zeQsL4eif20w+CWgvPNrS6iXUIs8W +w5/fHsxwmrkG96nDkMErJKhmjmLpC+YdbiMe6P/KIpas09m08RTIqcz7ua0Xm3ey +ndU+8ILJOhcnWV55W43nTw/UUFse7f+qY61n7kcd1sGd7ZfSEdEIqS3K2vEtA3ER +fn0s3cyXVEBxL9OZqcAk45bCFVOl13Fp/DBfquHEjvAyeg0= +-----END AGE ENCRYPTED FILE-----` ) func TestMasterKeysFromRecipients(t *testing.T) { @@ -400,3 +413,92 @@ func TestUserConfigDir(t *testing.T) { assert.Equal(t, home, dir) } } + +func TestMasterKey_Identities_Passphrase(t *testing.T) { + t.Run(SopsAgeKeyEnv, func(t *testing.T) { + key := &MasterKey{EncryptedKey: mockEncryptedKey} + t.Setenv(SopsAgeKeyEnv, mockEncryptedIdentity) + //blocks calling gpg-agent + os.Unsetenv("XDG_RUNTIME_DIR") + + funcDefer, _ := mockStdin(t, mockIdentityPassphrase) + defer funcDefer() + + got, err := key.Decrypt() + + assert.NoError(t, err) + assert.EqualValues(t, mockEncryptedKeyPlain, got) + }) + + t.Run(SopsAgeKeyFileEnv, func(t *testing.T) { + tmpDir := t.TempDir() + // Overwrite to ensure local config is not picked up by tests + overwriteUserConfigDir(t, tmpDir) + + keyPath := filepath.Join(tmpDir, "keys.txt") + assert.NoError(t, os.WriteFile(keyPath, []byte(mockEncryptedIdentity), 0o644)) + + key := &MasterKey{EncryptedKey: mockEncryptedKey} + t.Setenv(SopsAgeKeyFileEnv, keyPath) + //blocks calling gpg-agent + os.Unsetenv("XDG_RUNTIME_DIR") + + funcDefer, _ := mockStdin(t, mockIdentityPassphrase) + defer funcDefer() + + got, err := key.Decrypt() + assert.NoError(t, err) + assert.EqualValues(t, mockEncryptedKeyPlain, got) + }) + + t.Run("invalid encrypted key", func(t *testing.T) { + key := &MasterKey{EncryptedKey: "invalid"} + t.Setenv(SopsAgeKeyEnv, mockEncryptedIdentity) + //blocks calling gpg-agent + os.Unsetenv("XDG_RUNTIME_DIR") + + funcDefer, _ := mockStdin(t, mockIdentityPassphrase) + defer funcDefer() + + got, err := key.Decrypt() + assert.Error(t, err) + assert.ErrorContains(t, err, "failed to create reader for decrypting sops data key with age") + assert.Nil(t, got) + }) +} + +// mockStdin is a helper function that lets the test pretend dummyInput as os.Stdin. +// It will return a function for `defer` to clean up after the test. +// +// Note: `ioutil.TempFile` should be replaced to `os.CreateTemp` for Go v1.16 or higher. +func mockStdin(t *testing.T, dummyInput string) (funcDefer func(), err error) { + t.Helper() + + oldOsStdin := os.Stdin + + fmt.Println(t.TempDir(), t.Name()) + + tmpfile, err := ioutil.TempFile(t.TempDir(), strings.Replace(t.Name(), "/", "_", -1)) + if err != nil { + return nil, err + } + + content := []byte(dummyInput) + + if _, err := tmpfile.Write(content); err != nil { + return nil, err + } + + if _, err := tmpfile.Seek(0, 0); err != nil { + return nil, err + } + + // Set stdin to the temp file + os.Stdin = tmpfile + + return func() { + // clean up + os.Stdin = oldOsStdin + os.Remove(tmpfile.Name()) + }, nil +} diff --git a/go.mod b/go.mod index 29f8dcd76..ac4f38b16 100644 --- a/go.mod +++ b/go.mod @@ -11,13 +11,13 @@ require ( github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.1 github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.3.0 github.com/ProtonMail/go-crypto v1.1.5 - github.com/aws/aws-sdk-go-v2 v1.33.0 - github.com/aws/aws-sdk-go-v2/config v1.29.1 - github.com/aws/aws-sdk-go-v2/credentials v1.17.54 - github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.53 - github.com/aws/aws-sdk-go-v2/service/kms v1.37.13 - github.com/aws/aws-sdk-go-v2/service/s3 v1.74.0 - github.com/aws/aws-sdk-go-v2/service/sts v1.33.9 + github.com/aws/aws-sdk-go-v2 v1.34.0 + github.com/aws/aws-sdk-go-v2/config v1.29.2 + github.com/aws/aws-sdk-go-v2/credentials v1.17.55 + github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.54 + github.com/aws/aws-sdk-go-v2/service/kms v1.37.14 + github.com/aws/aws-sdk-go-v2/service/s3 v1.74.1 + github.com/aws/aws-sdk-go-v2/service/sts v1.33.10 github.com/blang/semver v3.5.1+incompatible github.com/fatih/color v1.18.0 github.com/getsops/gopgagent v0.0.0-20241224165529-7044f28e491e @@ -64,19 +64,19 @@ require ( github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.49.0 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect - github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.7 // indirect - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.24 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.28 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.28 // indirect - github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 // indirect - github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.28 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.5.2 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.9 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.9 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.24.11 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.10 // indirect - github.com/aws/smithy-go v1.22.1 // indirect + github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.8 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.25 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.29 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.29 // indirect + github.com/aws/aws-sdk-go-v2/internal/ini v1.8.2 // indirect + github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.29 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.2 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.5.3 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.10 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.10 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.24.12 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.11 // indirect + github.com/aws/smithy-go v1.22.2 // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/census-instrumentation/opencensus-proto v0.4.1 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect diff --git a/go.sum b/go.sum index 1174039e7..714de6051 100644 --- a/go.sum +++ b/go.sum @@ -63,46 +63,46 @@ github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEV github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk= github.com/ProtonMail/go-crypto v1.1.5 h1:eoAQfK2dwL+tFSFpr7TbOaPNUbPiJj4fLYwwGE1FQO4= github.com/ProtonMail/go-crypto v1.1.5/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE= -github.com/aws/aws-sdk-go-v2 v1.33.0 h1:Evgm4DI9imD81V0WwD+TN4DCwjUMdc94TrduMLbgZJs= -github.com/aws/aws-sdk-go-v2 v1.33.0/go.mod h1:P5WJBrYqqbWVaOxgH0X/FYYD47/nooaPOZPlQdmiN2U= -github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.7 h1:lL7IfaFzngfx0ZwUGOZdsFFnQ5uLvR0hWqqhyE7Q9M8= -github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.7/go.mod h1:QraP0UcVlQJsmHfioCrveWOC1nbiWUl3ej08h4mXWoc= -github.com/aws/aws-sdk-go-v2/config v1.29.1 h1:JZhGawAyZ/EuJeBtbQYnaoftczcb2drR2Iq36Wgz4sQ= -github.com/aws/aws-sdk-go-v2/config v1.29.1/go.mod h1:7bR2YD5euaxBhzt2y/oDkt3uNRb6tjFp98GlTFueRwk= -github.com/aws/aws-sdk-go-v2/credentials v1.17.54 h1:4UmqeOqJPvdvASZWrKlhzpRahAulBfyTJQUaYy4+hEI= -github.com/aws/aws-sdk-go-v2/credentials v1.17.54/go.mod h1:RTdfo0P0hbbTxIhmQrOsC/PquBZGabEPnCaxxKRPSnI= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.24 h1:5grmdTdMsovn9kPZPI23Hhvp0ZyNm5cRO+IZFIYiAfw= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.24/go.mod h1:zqi7TVKTswH3Ozq28PkmBmgzG1tona7mo9G2IJg4Cis= -github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.53 h1:3jYpOndmkKtmlPOhMNIV7Q92GD61x/KNjmxUcB95btw= -github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.53/go.mod h1:+s7tPUl4uy7FMpT5qnjkY5YJNuKU2HZL6trkYxQNtb4= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.28 h1:igORFSiH3bfq4lxKFkTSYDhJEUCYo6C8VKiWJjYwQuQ= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.28/go.mod h1:3So8EA/aAYm36L7XIvCVwLa0s5N0P7o2b1oqnx/2R4g= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.28 h1:1mOW9zAUMhTSrMDssEHS/ajx8JcAj/IcftzcmNlmVLI= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.28/go.mod h1:kGlXVIWDfvt2Ox5zEaNglmq0hXPHgQFNMix33Tw22jA= -github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 h1:VaRN3TlFdd6KxX1x3ILT5ynH6HvKgqdiXoTxAF4HQcQ= -github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1/go.mod h1:FbtygfRFze9usAadmnGJNc8KsP346kEe+y2/oyhGAGc= -github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.28 h1:7kpeALOUeThs2kEjlAxlADAVfxKmkYAedlpZ3kdoSJ4= -github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.28/go.mod h1:pyaOYEdp1MJWgtXLy6q80r3DhsVdOIOZNB9hdTcJIvI= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1 h1:iXtILhvDxB6kPvEXgsDhGaZCSC6LQET5ZHSdJozeI0Y= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1/go.mod h1:9nu0fVANtYiAePIBh2/pFUSwtJ402hLnp854CNoDOeE= -github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.5.2 h1:e6um6+DWYQP1XCa+E9YVtG/9v1qk5lyAOelMOVwSyO8= -github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.5.2/go.mod h1:dIW8puxSbYLSPv/ju0d9A3CpwXdtqvJtYKDMVmPLOWE= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.9 h1:TQmKDyETFGiXVhZfQ/I0cCFziqqX58pi4tKJGYGFSz0= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.9/go.mod h1:HVLPK2iHQBUx7HfZeOQSEu3v2ubZaAY2YPbAm5/WUyY= -github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.9 h1:2aInXbh02XsbO0KobPGMNXyv2QP73VDKsWPNJARj/+4= -github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.9/go.mod h1:dgXS1i+HgWnYkPXqNoPIPKeUsUUYHaUbThC90aDnNiE= -github.com/aws/aws-sdk-go-v2/service/kms v1.37.13 h1:JJHYuosiaMHr9V8m+v6UPmM7ZWHP+l8cv/xEG9OQTuE= -github.com/aws/aws-sdk-go-v2/service/kms v1.37.13/go.mod h1:TTGECZ6vGfx8k/pmzQKokSJy7ux2PJID4r96QCh5L0A= -github.com/aws/aws-sdk-go-v2/service/s3 v1.74.0 h1:ncCHiFU9Eq4qnKCNlzMZXfFmvb9R8OVNfU8SFOskxdI= -github.com/aws/aws-sdk-go-v2/service/s3 v1.74.0/go.mod h1:jGJ/v7FIi7Ys9t54tmEFnrxuaWeJLpwNgKp2DXAVhOU= -github.com/aws/aws-sdk-go-v2/service/sso v1.24.11 h1:kuIyu4fTT38Kj7YCC7ouNbVZSSpqkZ+LzIfhCr6Dg+I= -github.com/aws/aws-sdk-go-v2/service/sso v1.24.11/go.mod h1:Ro744S4fKiCCuZECXgOi760TiYylUM8ZBf6OGiZzJtY= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.10 h1:l+dgv/64iVlQ3WsBbnn+JSbkj01jIi+SM0wYsj3y/hY= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.10/go.mod h1:Fzsj6lZEb8AkTE5S68OhcbBqeWPsR8RnGuKPr8Todl8= -github.com/aws/aws-sdk-go-v2/service/sts v1.33.9 h1:BRVDbewN6VZcwr+FBOszDKvYeXY1kJ+GGMCcpghlw0U= -github.com/aws/aws-sdk-go-v2/service/sts v1.33.9/go.mod h1:f6vjfZER1M17Fokn0IzssOTMT2N8ZSq+7jnNF0tArvw= -github.com/aws/smithy-go v1.22.1 h1:/HPHZQ0g7f4eUeK6HKglFz8uwVfZKgoI25rb/J+dnro= -github.com/aws/smithy-go v1.22.1/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg= +github.com/aws/aws-sdk-go-v2 v1.34.0 h1:9iyL+cjifckRGEVpRKZP3eIxVlL06Qk1Tk13vreaVQU= +github.com/aws/aws-sdk-go-v2 v1.34.0/go.mod h1:JgstGg0JjWU1KpVJjD5H0y0yyAIpSdKEq556EI6yOOM= +github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.8 h1:zAxi9p3wsZMIaVCdoiQp2uZ9k1LsZvmAnoTBeZPXom0= +github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.8/go.mod h1:3XkePX5dSaxveLAYY7nsbsZZrKxCyEuE5pM4ziFxyGg= +github.com/aws/aws-sdk-go-v2/config v1.29.2 h1:JuIxOEPcSKpMB0J+khMjznG9LIhIBdmqNiEcPclnwqc= +github.com/aws/aws-sdk-go-v2/config v1.29.2/go.mod h1:HktTHregOZwNSM/e7WTfVSu9RCX+3eOv+6ij27PtaYs= +github.com/aws/aws-sdk-go-v2/credentials v1.17.55 h1:CDhKnDEaGkLA5ZszV/qw5uwN5M8rbv9Cl0JRN+PRsaM= +github.com/aws/aws-sdk-go-v2/credentials v1.17.55/go.mod h1:kPD/vj+RB5MREDUky376+zdnjZpR+WgdBBvwrmnlmKE= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.25 h1:kU7tmXNaJ07LsyN3BUgGqAmVmQtq0w6duVIHAKfp0/w= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.25/go.mod h1:OiC8+OiqrURb1wrwmr/UbOVLFSWEGxjinj5C299VQdo= +github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.54 h1:6BWOAho3Cgdy4cmNJ4HWY8VZgqODEU7Gw78XXireNZI= +github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.54/go.mod h1:n+t/oyYErOV3jf/GxNTVlizSM9RMV1yH7jvcIvld3Do= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.29 h1:Ej0Rf3GMv50Qh4G4852j2djtoDb7AzQ7MuQeFHa3D70= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.29/go.mod h1:oeNTC7PwJNoM5AznVr23wxhLnuJv0ZDe5v7w0wqIs9M= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.29 h1:6e8a71X+9GfghragVevC5bZqvATtc3mAMgxpSNbgzF0= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.29/go.mod h1:c4jkZiQ+BWpNqq7VtrxjwISrLrt/VvPq3XiopkUIolI= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.2 h1:Pg9URiobXy85kgFev3og2CuOZ8JZUBENF+dcgWBaYNk= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.2/go.mod h1:FbtygfRFze9usAadmnGJNc8KsP346kEe+y2/oyhGAGc= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.29 h1:g9OUETuxA8i/Www5Cby0R3WSTe7ppFTZXHVLNskNS4w= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.29/go.mod h1:CQk+koLR1QeY1+vm7lqNfFii07DEderKq6T3F1L2pyc= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.2 h1:D4oz8/CzT9bAEYtVhSBmFj2dNOtaHOtMKc2vHBwYizA= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.2/go.mod h1:Za3IHqTQ+yNcRHxu1OFucBh0ACZT4j4VQFF0BqpZcLY= +github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.5.3 h1:EP1ITDgYVPM2dL1bBBntJ7AW5yTjuWGz9XO+CZwpALU= +github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.5.3/go.mod h1:5lWNWeAgWenJ/BZ/CP9k9DjLbC0pjnM045WjXRPPi14= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.10 h1:hN4yJBGswmFTOVYqmbz1GBs9ZMtQe8SrYxPwrkrlRv8= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.10/go.mod h1:TsxON4fEZXyrKY+D+3d2gSTyJkGORexIYab9PTf56DA= +github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.10 h1:fXoWC2gi7tdJYNTPnnlSGzEVwewUchOi8xVq/dkg8Qs= +github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.10/go.mod h1:cvzBApD5dVazHU8C2rbBQzzzsKc8m5+wNJ9mCRZLKPc= +github.com/aws/aws-sdk-go-v2/service/kms v1.37.14 h1:IvhYu4W4wKMqN6DqtuVD7obkFflgTv1wmnZMjlSeDAA= +github.com/aws/aws-sdk-go-v2/service/kms v1.37.14/go.mod h1:yqUt1GZH4uf7HUNT2Kd7qk6P+Vi5z+C5+NjNSNRO1L4= +github.com/aws/aws-sdk-go-v2/service/s3 v1.74.1 h1:9LawY3cDJ3HE+v2GMd5SOkNLDwgN4K7TsCjyVBYu/L4= +github.com/aws/aws-sdk-go-v2/service/s3 v1.74.1/go.mod h1:hHnELVnIHltd8EOF3YzahVX6F6y2C6dNqpRj1IMkS5I= +github.com/aws/aws-sdk-go-v2/service/sso v1.24.12 h1:kznaW4f81mNMlREkU9w3jUuJvU5g/KsqDV43ab7Rp6s= +github.com/aws/aws-sdk-go-v2/service/sso v1.24.12/go.mod h1:bZy9r8e0/s0P7BSDHgMLXK2KvdyRRBIQ2blKlvLt0IU= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.11 h1:mUwIpAvILeKFnRx4h1dEgGEFGuV8KJ3pEScZWVFYuZA= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.11/go.mod h1:JDJtD+b8HNVv71axz8+S5492KM8wTzHRFpMKQbPlYxw= +github.com/aws/aws-sdk-go-v2/service/sts v1.33.10 h1:g9d+TOsu3ac7SgmY2dUf1qMgu/uJVTlQ4VCbH6hRxSw= +github.com/aws/aws-sdk-go-v2/service/sts v1.33.10/go.mod h1:WZfNmntu92HO44MVZAubQaz3qCuIdeOdog2sADfU6hU= +github.com/aws/smithy-go v1.22.2 h1:6D9hW43xKFrRx/tXXfAlIZc4JI+yQe6snnWcQyxSyLQ= +github.com/aws/smithy-go v1.22.2/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg= github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ= github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=