Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions age/keysource.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"filippo.io/age/agessh"
"filippo.io/age/armor"
"filippo.io/age/plugin"
"filippo.io/age/tag"
"github.com/sirupsen/logrus"
"golang.org/x/crypto/ssh"

Expand Down Expand Up @@ -488,6 +489,13 @@ func (key *MasterKey) loadIdentities() (ParsedIdentities, []string, errSet) {
// key or a public ssh key.
func parseRecipient(recipient string) (age.Recipient, error) {
switch {
case strings.HasPrefix(recipient, "age1tag1") || strings.HasPrefix(recipient, "age1tagpq1"):
parsedRecipient, err := tag.ParseRecipient(recipient)
if err != nil {
return nil, fmt.Errorf("failed to parse input as Bech32-encoded age tagged public key: %w", err)
}

return parsedRecipient, nil
case strings.HasPrefix(recipient, "age1pq1"):
parsedRecipient, err := age.ParseHybridRecipient(recipient)
if err != nil {
Expand Down
26 changes: 26 additions & 0 deletions age/keysource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import (
const (
// mockRecipient is a mock age recipient, it matches mockIdentity.
mockRecipient string = "age1lzd99uklcjnc0e7d860axevet2cz99ce9pq6tzuzd05l5nr28ams36nvun"
// mockTagRecipient is a mock age tagged recipient.
mockTagRecipient string = "age1tag1qv0h7q4p5zr5g0cy8rduxr8twtjgu2td4ufsyjgmrgm4zv655fk2v24507v"
// mockIdentity is a mock age identity.
mockIdentity string = "AGE-SECRET-KEY-1G0Q5K9TV4REQ3ZSQRMTMG8NSWQGYT0T7TZ33RAZEE0GZYVZN0APSU24RK7"
// mockHybridIdentity is a mock post-quantum age identity using a hybrid ML-KEM-768 KEM.
Expand Down Expand Up @@ -577,6 +579,30 @@ func TestMasterKey_loadIdentities(t *testing.T) {
})
}

func TestMasterKeyFromRecipient_TaggedRecipient(t *testing.T) {
t.Run("encrypts without an age-plugin-tag binary", func(t *testing.T) {
// Empty PATH: a regression to the plugin path would try to exec
// age-plugin-tag here and fail.
t.Setenv("PATH", t.TempDir())

key, err := MasterKeyFromRecipient(mockTagRecipient)
assert.NoError(t, err)

assert.NoError(t, key.Encrypt([]byte("data")))
assert.NotEmpty(t, key.EncryptedKey)
})

t.Run("routes malformed age1tag to the tag parser, not the plugin", func(t *testing.T) {
_, err := MasterKeyFromRecipient("age1tag1qqqq")
assert.ErrorContains(t, err, "tagged")
})

t.Run("routes malformed age1tagpq to the tag parser, not the plugin", func(t *testing.T) {
_, err := MasterKeyFromRecipient("age1tagpq1qqqq")
assert.ErrorContains(t, err, "tagged")
})
}

// overwriteUserConfigDir sets the user config directory and the user home directory
// based on the os.UserConfigDir logic.
func overwriteUserConfigDir(t *testing.T, path string) {
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ require (
dario.cat/mergo v1.0.2 // indirect
filippo.io/edwards25519 v1.2.0 // indirect
filippo.io/hpke v0.4.0 // indirect
filippo.io/nistec v0.0.4 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v1.12.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.2.0 // indirect
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ filippo.io/edwards25519 v1.2.0 h1:crnVqOiS4jqYleHd9vaKZ+HKtHfllngJIiOpNpoJsjo=
filippo.io/edwards25519 v1.2.0/go.mod h1:xzAOLCNug/yB62zG1bQ8uziwrIqIuxhctzJT18Q77mc=
filippo.io/hpke v0.4.0 h1:p575VVQ6ted4pL+it6M00V/f2qTZITO0zgmdKCkd5+A=
filippo.io/hpke v0.4.0/go.mod h1:EmAN849/P3qdeK+PCMkDpDm83vRHM5cDipBJ8xbQLVY=
filippo.io/nistec v0.0.4 h1:F14ZHT5htWlMnQVPndX9ro9arf56cBhQxq4LnDI491s=
filippo.io/nistec v0.0.4/go.mod h1:PK/lw8I1gQT4hUML4QGaqljwdDaFcMyFKSXN7kjrtKI=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.22.0 h1:aokoqcHvaGjiM3VpjKDfMMnF/8epJ+Q1HLJ7CudztqE=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.22.0/go.mod h1:/WYEx9pcM9Y+Dd/APJaNlSvVSvzl54rrMdZT5+Oi2LM=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.14.0 h1:CU4+EJeJi3TKYWEcYuSdWsjzw0nVsK/H0MSQOiPcymU=
Expand Down