diff --git a/plugin/client.go b/plugin/client.go index 6f97167..b4e0ff7 100644 --- a/plugin/client.go +++ b/plugin/client.go @@ -14,13 +14,11 @@ import ( "io" "os" "strconv" - "strings" "time" exec "golang.org/x/sys/execabs" "filippo.io/age" - "filippo.io/age/internal/bech32" "filippo.io/age/internal/format" ) @@ -36,14 +34,10 @@ type Recipient struct { var _ age.Recipient = &Recipient{} func NewRecipient(s string, ui *ClientUI) (*Recipient, error) { - hrp, _, err := bech32.Decode(s) + name, _, err := ParseRecipient(s) if err != nil { - return nil, fmt.Errorf("invalid recipient encoding %q: %v", s, err) + return nil, err } - if !strings.HasPrefix(hrp, "age1") { - return nil, fmt.Errorf("not a plugin recipient %q: %v", s, err) - } - name := strings.TrimPrefix(hrp, "age1") return &Recipient{ name: name, encoding: s, ui: ui, }, nil @@ -151,25 +145,17 @@ type Identity struct { var _ age.Identity = &Identity{} func NewIdentity(s string, ui *ClientUI) (*Identity, error) { - hrp, _, err := bech32.Decode(s) + name, _, err := ParseIdentity(s) if err != nil { - return nil, fmt.Errorf("invalid identity encoding: %v", err) + return nil, err } - if !strings.HasPrefix(hrp, "AGE-PLUGIN-") || !strings.HasSuffix(hrp, "-") { - return nil, fmt.Errorf("not a plugin identity: %v", err) - } - name := strings.TrimSuffix(strings.TrimPrefix(hrp, "AGE-PLUGIN-"), "-") - name = strings.ToLower(name) return &Identity{ name: name, encoding: s, ui: ui, }, nil } func NewIdentityWithoutData(name string, ui *ClientUI) (*Identity, error) { - s, err := bech32.Encode("AGE-PLUGIN-"+strings.ToUpper(name)+"-", nil) - if err != nil { - return nil, err - } + s := EncodeIdentity(name, nil) return &Identity{ name: name, encoding: s, ui: ui, }, nil diff --git a/plugin/encode.go b/plugin/encode.go new file mode 100644 index 0000000..5000708 --- /dev/null +++ b/plugin/encode.go @@ -0,0 +1,55 @@ +// Copyright 2023 The age Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package plugin + +import ( + "fmt" + "strings" + + "filippo.io/age/internal/bech32" +) + +// EncodeIdentity encodes a plugin identity string for a plugin with the given +// name. If the name is invalid, it returns an empty string. +func EncodeIdentity(name string, data []byte) string { + s, _ := bech32.Encode("AGE-PLUGIN-"+strings.ToUpper(name)+"-", data) + return s +} + +// ParseIdentity decodes a plugin identity string. It returns the plugin name +// in lowercase and the encoded data. +func ParseIdentity(s string) (name string, data []byte, err error) { + hrp, data, err := bech32.Decode(s) + if err != nil { + return "", nil, fmt.Errorf("invalid identity encoding: %v", err) + } + if !strings.HasPrefix(hrp, "AGE-PLUGIN-") || !strings.HasSuffix(hrp, "-") { + return "", nil, fmt.Errorf("not a plugin identity: %v", err) + } + name = strings.TrimSuffix(strings.TrimPrefix(hrp, "AGE-PLUGIN-"), "-") + name = strings.ToLower(name) + return name, data, nil +} + +// EncodeRecipient encodes a plugin recipient string for a plugin with the given +// name. If the name is invalid, it returns an empty string. +func EncodeRecipient(name string, data []byte) string { + s, _ := bech32.Encode("age1"+strings.ToLower(name), data) + return s +} + +// ParseRecipient decodes a plugin recipient string. It returns the plugin name +// in lowercase and the encoded data. +func ParseRecipient(s string) (name string, data []byte, err error) { + hrp, data, err := bech32.Decode(s) + if err != nil { + return "", nil, fmt.Errorf("invalid recipient encoding: %v", err) + } + if !strings.HasPrefix(hrp, "age1") { + return "", nil, fmt.Errorf("not a plugin recipient: %v", err) + } + name = strings.TrimPrefix(hrp, "age1") + return name, data, nil +}