Documentation
¶
Overview ¶
Package age implements file encryption according to the age-encryption.org/v1 specification.
For most use cases, use the Encrypt and Decrypt functions with HybridRecipient and HybridIdentity. If passphrase encryption is required, use ScryptRecipient and ScryptIdentity. For compatibility with existing SSH keys use the filippo.io/age/agessh package.
age encrypted files are binary and not malleable. For encoding them as text, use the filippo.io/age/armor package.
Key management ¶
age does not have a global keyring. Instead, since age keys are small, textual, and cheap, you are encouraged to generate dedicated keys for each task and application.
Recipient public keys can be passed around as command line flags and in config files, while secret keys should be stored in dedicated files, through secret management systems, or as environment variables.
There is no default path for age keys. Instead, they should be stored at application-specific paths. The CLI supports files where private keys are listed one per line, ignoring empty lines and lines starting with "#". These files can be parsed with ParseIdentities.
When integrating age into a new system, it's recommended that you only support native (X25519 and hybrid) keys, and not SSH keys. The latter are supported for manual encryption operations. If you need to tie into existing key management infrastructure, you might want to consider implementing your own Recipient and Identity.
Backwards compatibility ¶
Files encrypted with a stable version (not alpha, beta, or release candidate) of age, or with any v1.0.0 beta or release candidate, will decrypt with any later versions of the v1 API. This might change in v2, in which case v1 will be maintained with security fixes for compatibility with older files.
If decrypting an older file poses a security risk, doing so might require an explicit opt-in in the API.
Index ¶
- Variables
- func Decrypt(src io.Reader, identities ...Identity) (io.Reader, error)
- func DecryptHeader(header []byte, identities ...Identity) ([]byte, error)
- func DecryptReaderAt(src io.ReaderAt, encryptedSize int64, identities ...Identity) (io.ReaderAt, int64, error)
- func Encrypt(dst io.Writer, recipients ...Recipient) (io.WriteCloser, error)
- func EncryptReader(src io.Reader, recipients ...Recipient) (io.Reader, error)
- func ExtractHeader(src io.Reader) ([]byte, error)
- type HybridIdentity
- type HybridRecipient
- type Identity
- type NoIdentityMatchError
- type PQKemType
- type Recipient
- type RecipientWithLabels
- type ScryptIdentity
- type ScryptRecipient
- type Stanza
- type X25519Identity
- type X25519Recipient
- type XWingIdentity
- type XWingRecipient
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ErrIncorrectIdentity = errors.New("incorrect identity for recipient block")
ErrIncorrectIdentity is returned by [Identity.Unwrap] if none of the recipient stanzas match the identity.
Functions ¶
func Decrypt ¶
Decrypt decrypts a file encrypted to one or more identities. All identities will be tried until one successfully decrypts the file. Native, non-interactive identities are tried before any other identities.
Decrypt returns a Reader reading the decrypted plaintext of the age file read from src. If no identity matches the encrypted file, the returned error will be of type NoIdentityMatchError.
Example ¶
package main
import (
"bytes"
"fmt"
"io"
"log"
"os"
"github.com/luxfi/age"
)
// DO NOT hardcode the private key. Store it in a secret storage solution,
// on disk if the local machine is trusted, or have the user provide it.
var privateKey string
func main() {
identity, err := age.ParseX25519Identity(privateKey)
if err != nil {
log.Fatalf("Failed to parse private key: %v", err)
}
f, err := os.Open("testdata/example.age")
if err != nil {
log.Fatalf("Failed to open file: %v", err)
}
r, err := age.Decrypt(f, identity)
if err != nil {
log.Fatalf("Failed to open encrypted file: %v", err)
}
out := &bytes.Buffer{}
if _, err := io.Copy(out, r); err != nil {
log.Fatalf("Failed to read encrypted file: %v", err)
}
fmt.Printf("File contents: %q\n", out.Bytes())
}
Output: File contents: "Black lives matter."
func DecryptHeader ¶
DecryptHeader decrypts a detached header and returns a file key.
The detached header can be produced by ExtractHeader, and the returned file key can be used with NewInjectedFileKeyIdentity.
This is a low-level function that most users won't need. It is the caller's responsibility to keep track of what file the returned file key decrypts, and to ensure the file key is not used for any other purpose.
func DecryptReaderAt ¶
func DecryptReaderAt(src io.ReaderAt, encryptedSize int64, identities ...Identity) (io.ReaderAt, int64, error)
DecryptReaderAt decrypts a file encrypted to one or more identities. All identities will be tried until one successfully decrypts the file. Native, non-interactive identities are tried before any other identities.
DecryptReaderAt takes an underlying io.ReaderAt and its total encrypted size, and returns a ReaderAt of the decrypted plaintext and the plaintext size. These can be used for example to instantiate an io.SectionReader, which implements io.Reader and io.Seeker, or for [zip.NewReader]. Note that ReaderAt by definition disregards the seek position of src.
The ReadAt method of the returned ReaderAt can be called concurrently. The ReaderAt will internally cache the most recently decrypted chunk. DecryptReaderAt reads and decrypts the final chunk before returning, to authenticate the plaintext size.
If no identity matches the encrypted file, the returned error will be of type NoIdentityMatchError.
Example ¶
package main
import (
"archive/zip"
"fmt"
"io/fs"
"log"
"os"
"github.com/luxfi/age"
)
// DO NOT hardcode the private key. Store it in a secret storage solution,
// on disk if the local machine is trusted, or have the user provide it.
var privateKey string
func main() {
identity, err := age.ParseX25519Identity(privateKey)
if err != nil {
log.Fatalf("Failed to parse private key: %v", err)
}
f, err := os.Open("testdata/example.zip.age")
if err != nil {
log.Fatalf("Failed to open file: %v", err)
}
stat, err := f.Stat()
if err != nil {
log.Fatalf("Failed to stat file: %v", err)
}
r, size, err := age.DecryptReaderAt(f, stat.Size(), identity)
if err != nil {
log.Fatalf("Failed to open encrypted file: %v", err)
}
z, err := zip.NewReader(r, size)
if err != nil {
log.Fatalf("Failed to open zip: %v", err)
}
contents, err := fs.ReadFile(z, "example.txt")
if err != nil {
log.Fatalf("Failed to read file from zip: %v", err)
}
fmt.Printf("File contents: %q\n", contents)
}
Output: File contents: "Black lives matter."
func Encrypt ¶
Encrypt encrypts a file to one or more recipients. Every recipient will be able to decrypt the file.
Writes to the returned WriteCloser are encrypted and written to dst as an age file. The caller must call Close on the WriteCloser when done for the last chunk to be encrypted and flushed to dst.
Example ¶
package main
import (
"bytes"
"fmt"
"io"
"log"
"github.com/luxfi/age"
)
func main() {
publicKey := "age1cy0su9fwf3gf9mw868g5yut09p6nytfmmnktexz2ya5uqg9vl9sss4euqm"
recipient, err := age.ParseX25519Recipient(publicKey)
if err != nil {
log.Fatalf("Failed to parse public key %q: %v", publicKey, err)
}
out := &bytes.Buffer{}
w, err := age.Encrypt(out, recipient)
if err != nil {
log.Fatalf("Failed to create encrypted file: %v", err)
}
if _, err := io.WriteString(w, "Black lives matter."); err != nil {
log.Fatalf("Failed to write to encrypted file: %v", err)
}
if err := w.Close(); err != nil {
log.Fatalf("Failed to close encrypted file: %v", err)
}
fmt.Printf("Encrypted file size: %d\n", out.Len())
}
Output: Encrypted file size: 219
func EncryptReader ¶
EncryptReader encrypts a file to one or more recipients. Every recipient will be able to decrypt the file.
Reads from the returned Reader produce the encrypted file, where the plaintext is read from src.
func ExtractHeader ¶
ExtractHeader returns a detached header from the src file.
The detached header can be decrypted with DecryptHeader (for example on a different system, without sharing the ciphertext) and then the file key can be used with NewInjectedFileKeyIdentity.
This is a low-level function that most users won't need.
Types ¶
type HybridIdentity ¶
type HybridIdentity struct {
// contains filtered or unexported fields
}
HybridIdentity is the standard age private key, which can decrypt messages encrypted to the corresponding HybridRecipient.
func GenerateHybridIdentity ¶
func GenerateHybridIdentity() (*HybridIdentity, error)
GenerateHybridIdentity randomly generates a new HybridIdentity.
func ParseHybridIdentity ¶
func ParseHybridIdentity(s string) (*HybridIdentity, error)
ParseHybridIdentity returns a new HybridIdentity from a Bech32 private key encoding with the "AGE-SECRET-KEY-PQ-1" prefix.
func (*HybridIdentity) Recipient ¶
func (i *HybridIdentity) Recipient() *HybridRecipient
Recipient returns the public HybridRecipient value corresponding to i.
func (*HybridIdentity) String ¶
func (i *HybridIdentity) String() string
String returns the Bech32 private key encoding of i.
type HybridRecipient ¶
type HybridRecipient struct {
// contains filtered or unexported fields
}
HybridRecipient is the standard age public key. Messages encrypted to this recipient can be decrypted with the corresponding HybridIdentity.
This recipient is safe against future cryptographically-relevant quantum computers, and can only be used along with other post-quantum recipients.
This recipient is anonymous, in the sense that an attacker can't tell from the message alone if it is encrypted to a certain recipient.
func ParseHybridRecipient ¶
func ParseHybridRecipient(s string) (*HybridRecipient, error)
ParseHybridRecipient returns a new HybridRecipient from a Bech32 public key encoding with the "age1pq1" prefix.
func (*HybridRecipient) String ¶
func (r *HybridRecipient) String() string
String returns the Bech32 public key encoding of r.
func (*HybridRecipient) WrapWithLabels ¶
func (r *HybridRecipient) WrapWithLabels(fileKey []byte) ([]*Stanza, []string, error)
WrapWithLabels implements RecipientWithLabels, returning a single "postquantum" label. This ensures a HybridRecipient can't be mixed with other recipients that would defeat its post-quantum security.
To unsafely bypass this restriction, wrap HybridRecipient in a Recipient type that doesn't expose WrapWithLabels.
type Identity ¶
type Identity interface {
// Unwrap must return an error wrapping [ErrIncorrectIdentity] if none of
// the recipient stanzas match the identity, any other error will be
// considered fatal.
//
// Most age API users won't need to interact with this method directly, and
// should instead pass [Identity] implementations to [Decrypt].
Unwrap(stanzas []*Stanza) (fileKey []byte, err error)
}
An Identity is passed to Decrypt to unwrap an opaque file key from a recipient stanza. It can be for example a secret key like HybridIdentity, a plugin, or a custom implementation.
func GeneratePQIdentity ¶ added in v1.5.0
GeneratePQIdentity generates a new post-quantum hybrid identity using the selected KEM. If kem is empty, the AGE_PQ_KEM environment variable is consulted; if that is also unset, X-Wing is used.
The returned Identity is either an *XWingIdentity or a *HybridIdentity; callers can type-assert if they need KEM-specific methods, but usually the generic Identity interface is sufficient.
Example:
id, err := age.GeneratePQIdentity(age.PQKemXWing)
if err != nil { … }
fmt.Println(id.(interface{ Recipient() Recipient }).Recipient())
func NewInjectedFileKeyIdentity ¶
NewInjectedFileKeyIdentity returns an Identity that always produces a fixed file key, allowing the use of a file key obtained out-of-band, for example via DecryptHeader.
This is a low-level function that most users won't need.
func ParseIdentities ¶
ParseIdentities parses a file with one or more private key encodings, one per line. Empty lines and lines starting with "#" are ignored.
This is the same syntax as the private key files accepted by the CLI, except the CLI also accepts SSH private keys, which are not recommended for the average application, and plugins, which involve invoking external programs.
Currently, all returned values are of type *X25519Identity, *HybridIdentity, or *XWingIdentity, but different types might be returned in the future.
Example ¶
package main
import (
"bytes"
"fmt"
"io"
"log"
"os"
"github.com/luxfi/age"
)
func main() {
keyFile, err := os.Open("testdata/example_keys.txt")
if err != nil {
log.Fatalf("Failed to open private keys file: %v", err)
}
identities, err := age.ParseIdentities(keyFile)
if err != nil {
log.Fatalf("Failed to parse private key: %v", err)
}
f, err := os.Open("testdata/example.age")
if err != nil {
log.Fatalf("Failed to open file: %v", err)
}
r, err := age.Decrypt(f, identities...)
if err != nil {
log.Fatalf("Failed to open encrypted file: %v", err)
}
out := &bytes.Buffer{}
if _, err := io.Copy(out, r); err != nil {
log.Fatalf("Failed to read encrypted file: %v", err)
}
fmt.Printf("File contents: %q\n", out.Bytes())
}
Output: File contents: "Black lives matter."
type NoIdentityMatchError ¶
type NoIdentityMatchError struct {
// Errors is a slice of all the errors returned to Decrypt by the Unwrap
// calls it made. They all wrap [ErrIncorrectIdentity].
Errors []error
// StanzaTypes are the first argument of each recipient stanza in the
// encrypted file's header.
StanzaTypes []string
}
NoIdentityMatchError is returned by Decrypt when none of the supplied identities match the encrypted file.
func (*NoIdentityMatchError) Error ¶
func (e *NoIdentityMatchError) Error() string
func (*NoIdentityMatchError) Unwrap ¶
func (e *NoIdentityMatchError) Unwrap() []error
type PQKemType ¶ added in v1.5.0
type PQKemType string
PQKemType selects between the two hybrid post-quantum KEMs that age supports. Both are first-class and produce distinct recipient prefixes:
age1pq1… → PQKemHPKEMLKEM768X25519 (HPKE RFC 9180 with MLKEM768-X25519) age1xw1… → PQKemXWing (IETF draft-connolly-cfrg-xwing-kem-10)
At parse time the selection is automatic from the Bech32 prefix, so callers that already have a key string don't need to set this. PQKemType is only needed at *keygen* time when callers want to choose which KEM to produce.
const ( // PQKemHPKEMLKEM768X25519 is the original post-quantum hybrid introduced // in age v1.3.0: HPKE (RFC 9180) with MLKEM768-X25519, HKDF-SHA256, // ChaCha20-Poly1305. Recipient prefix: age1pq1, identity prefix: // AGE-SECRET-KEY-PQ-1. PQKemHPKEMLKEM768X25519 PQKemType = "hpke-mlkem768x25519" // PQKemXWing is the X-Wing KEM per IETF draft-connolly-cfrg-xwing-kem-10. // Simpler combiner (SHA3-256 direct) with the 6-byte XWingLabel, smaller // proof surface, HPKE KEM codepoint 25722. Recipient prefix: age1xw1, // identity prefix: AGE-SECRET-KEY-XW-1. PQKemXWing PQKemType = "xwing" // DefaultPQKemEnv is the environment variable consulted by // [GeneratePQIdentity] when callers pass an empty PQKemType. DefaultPQKemEnv = "AGE_PQ_KEM" )
func SupportedPQKems ¶ added in v1.5.0
func SupportedPQKems() []PQKemType
SupportedPQKems returns the list of post-quantum KEM types supported by this build of age. Useful for CLIs that enumerate options.
type Recipient ¶
type Recipient interface {
// Most age API users won't need to interact with this method directly, and
// should instead pass [Recipient] implementations to [Encrypt].
Wrap(fileKey []byte) ([]*Stanza, error)
}
A Recipient is passed to Encrypt to wrap an opaque file key to one or more recipient stanza(s). It can be for example a public key like HybridRecipient, a plugin, or a custom implementation.
func ParseRecipients ¶
ParseRecipients parses a file with one or more public key encodings, one per line. Empty lines and lines starting with "#" are ignored.
This is the same syntax as the recipients files accepted by the CLI, except the CLI also accepts SSH recipients, which are not recommended for the average application, tagged recipients, which have different privacy properties, and plugins, which involve invoking external programs.
Currently, all returned values are of type *X25519Recipient, *HybridRecipient, or *XWingRecipient, but different types might be returned in the future.
type RecipientWithLabels ¶
type RecipientWithLabels interface {
WrapWithLabels(fileKey []byte) (s []*Stanza, labels []string, err error)
}
RecipientWithLabels can be optionally implemented by a Recipient, in which case Encrypt will use WrapWithLabels instead of [Recipient.Wrap].
Encrypt will succeed only if the labels returned by all the recipients (assuming the empty set for those that don't implement RecipientWithLabels) are the same.
This can be used to ensure a recipient is only used with other recipients with equivalent properties (for example by setting a "postquantum" label) or to ensure a recipient is always used alone (by returning a random label, for example to preserve its authentication properties).
type ScryptIdentity ¶
type ScryptIdentity struct {
// contains filtered or unexported fields
}
ScryptIdentity is a password-based identity.
func NewScryptIdentity ¶
func NewScryptIdentity(password string) (*ScryptIdentity, error)
NewScryptIdentity returns a new ScryptIdentity with the provided password.
func (*ScryptIdentity) SetMaxWorkFactor ¶
func (i *ScryptIdentity) SetMaxWorkFactor(logN int)
SetMaxWorkFactor sets the maximum accepted scrypt work factor to 2^logN. It must be called before Unwrap.
This caps the amount of work that Decrypt might have to do to process received files. If SetMaxWorkFactor is not called, a fairly high default is used, which might not be suitable for systems processing untrusted files.
type ScryptRecipient ¶
type ScryptRecipient struct {
// contains filtered or unexported fields
}
ScryptRecipient is a password-based recipient. Anyone with the password can decrypt the message.
If a ScryptRecipient is used, it must be the only recipient for the file: it can't be mixed with other recipient types and can't be used multiple times for the same file.
Its use is not recommended for automated systems, which should prefer HybridRecipient or X25519Recipient.
func NewScryptRecipient ¶
func NewScryptRecipient(password string) (*ScryptRecipient, error)
NewScryptRecipient returns a new ScryptRecipient with the provided password.
func (*ScryptRecipient) SetWorkFactor ¶
func (r *ScryptRecipient) SetWorkFactor(logN int)
SetWorkFactor sets the scrypt work factor to 2^logN. It must be called before Wrap.
If SetWorkFactor is not called, a reasonable default is used.
func (*ScryptRecipient) WrapWithLabels ¶
func (r *ScryptRecipient) WrapWithLabels(fileKey []byte) (stanzas []*Stanza, labels []string, err error)
WrapWithLabels implements age.RecipientWithLabels, returning a random label. This ensures a ScryptRecipient can't be mixed with other recipients (including other ScryptRecipients).
Users reasonably expect files encrypted to a passphrase to be authenticated by that passphrase, i.e. for it to be impossible to produce a file that decrypts successfully with a passphrase without knowing it. If a file is encrypted to other recipients, those parties can produce different files that would break that expectation.
type Stanza ¶
A Stanza is a section of the age header that encapsulates the file key as encrypted to a specific recipient.
Most age API users won't need to interact with this type directly, and should instead pass Recipient implementations to Encrypt and Identity implementations to Decrypt.
type X25519Identity ¶
type X25519Identity struct {
// contains filtered or unexported fields
}
X25519Identity is the standard pre-quantum age private key, which can decrypt messages encrypted to the corresponding X25519Recipient. For post-quantum resistance, use HybridIdentity.
func GenerateX25519Identity ¶
func GenerateX25519Identity() (*X25519Identity, error)
GenerateX25519Identity randomly generates a new X25519Identity.
Example ¶
package main
import (
"fmt"
"log"
"github.com/luxfi/age"
)
func main() {
identity, err := age.GenerateX25519Identity()
if err != nil {
log.Fatalf("Failed to generate key pair: %v", err)
}
fmt.Printf("Public key: %s...\n", identity.Recipient().String()[:4])
fmt.Printf("Private key: %s...\n", identity.String()[:16])
}
Output: Public key: age1... Private key: AGE-SECRET-KEY-1...
func ParseX25519Identity ¶
func ParseX25519Identity(s string) (*X25519Identity, error)
ParseX25519Identity returns a new X25519Identity from a Bech32 private key encoding with the "AGE-SECRET-KEY-1" prefix.
func (*X25519Identity) Recipient ¶
func (i *X25519Identity) Recipient() *X25519Recipient
Recipient returns the public X25519Recipient value corresponding to i.
func (*X25519Identity) String ¶
func (i *X25519Identity) String() string
String returns the Bech32 private key encoding of i.
type X25519Recipient ¶
type X25519Recipient struct {
// contains filtered or unexported fields
}
X25519Recipient is the standard age pre-quantum public key. Messages encrypted to this recipient can be decrypted with the corresponding X25519Identity. For post-quantum resistance, use HybridRecipient.
This recipient is anonymous, in the sense that an attacker can't tell from the message alone if it is encrypted to a certain recipient.
func ParseX25519Recipient ¶
func ParseX25519Recipient(s string) (*X25519Recipient, error)
ParseX25519Recipient returns a new X25519Recipient from a Bech32 public key encoding with the "age1" prefix.
func (*X25519Recipient) String ¶
func (r *X25519Recipient) String() string
String returns the Bech32 public key encoding of r.
type XWingIdentity ¶ added in v1.5.0
type XWingIdentity struct {
// contains filtered or unexported fields
}
XWingIdentity is a post-quantum age private key using the X-Wing KEM, which can decrypt messages encrypted to the corresponding XWingRecipient.
func GenerateXWingIdentity ¶ added in v1.5.0
func GenerateXWingIdentity() (*XWingIdentity, error)
GenerateXWingIdentity randomly generates a new XWingIdentity.
func ParseXWingIdentity ¶ added in v1.5.0
func ParseXWingIdentity(s string) (*XWingIdentity, error)
ParseXWingIdentity returns a new XWingIdentity from a Bech32 private key encoding with the "AGE-SECRET-KEY-XW-1" prefix.
func (*XWingIdentity) Recipient ¶ added in v1.5.0
func (i *XWingIdentity) Recipient() *XWingRecipient
Recipient returns the public XWingRecipient value corresponding to i.
func (*XWingIdentity) String ¶ added in v1.5.0
func (i *XWingIdentity) String() string
String returns the Bech32 private key encoding of i.
type XWingRecipient ¶ added in v1.5.0
type XWingRecipient struct {
// contains filtered or unexported fields
}
XWingRecipient is a post-quantum age public key using the X-Wing KEM (IETF draft-connolly-cfrg-xwing-kem-10). Messages encrypted to this recipient can be decrypted with the corresponding XWingIdentity.
X-Wing combines ML-KEM-768 and X25519 in a standalone KEM with a SHA3-256 combiner, distinct from the HPKE-based HybridRecipient.
func ParseXWingRecipient ¶ added in v1.5.0
func ParseXWingRecipient(s string) (*XWingRecipient, error)
ParseXWingRecipient returns a new XWingRecipient from a Bech32 public key encoding with the "age1xw1" prefix.
func (*XWingRecipient) String ¶ added in v1.5.0
func (r *XWingRecipient) String() string
String returns the Bech32 public key encoding of r with "age1xw" prefix.
func (*XWingRecipient) Wrap ¶ added in v1.5.0
func (r *XWingRecipient) Wrap(fileKey []byte) ([]*Stanza, error)
func (*XWingRecipient) WrapWithLabels ¶ added in v1.5.0
func (r *XWingRecipient) WrapWithLabels(fileKey []byte) ([]*Stanza, []string, error)
WrapWithLabels implements RecipientWithLabels, returning a single "postquantum" label. This ensures an XWingRecipient can be mixed with other post-quantum recipients (like HybridRecipient) but not with classic X25519 recipients.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package agessh provides age.Identity and age.Recipient implementations of types "ssh-rsa" and "ssh-ed25519", which allow reusing existing SSH keys for encryption with age-encryption.org/v1.
|
Package agessh provides age.Identity and age.Recipient implementations of types "ssh-rsa" and "ssh-ed25519", which allow reusing existing SSH keys for encryption with age-encryption.org/v1. |
|
Package armor provides a strict, streaming implementation of the ASCII armoring format for age files.
|
Package armor provides a strict, streaming implementation of the ASCII armoring format for age files. |
|
cmd
|
|
|
age
command
|
|
|
age-inspect
command
|
|
|
age-keygen
command
|
|
|
age-plugin-batchpass
command
|
|
|
extra
|
|
|
age-plugin-pq
command
|
|
|
age-plugin-tag
command
|
|
|
age-plugin-tagpq
command
|
|
|
internal
|
|
|
bech32
Package bech32 is a modified version of the reference implementation of BIP173.
|
Package bech32 is a modified version of the reference implementation of BIP173. |
|
format
Package format implements the age file format.
|
Package format implements the age file format. |
|
stream
Package stream implements a variant of the STREAM chunked encryption scheme.
|
Package stream implements a variant of the STREAM chunked encryption scheme. |
|
Package plugin implements the age plugin protocol.
|
Package plugin implements the age plugin protocol. |
|
Package tag implements tagged P-256 or hybrid P-256 + ML-KEM-768 recipients, which can be used with identities stored on hardware keys, usually supported by dedicated plugins.
|
Package tag implements tagged P-256 or hybrid P-256 + ML-KEM-768 recipients, which can be used with identities stored on hardware keys, usually supported by dedicated plugins. |
|
internal/age-plugin-tagtest
command
Command age-plugin-tagtest is a that decrypts files encrypted to fixed age1tag1...
|
Command age-plugin-tagtest is a that decrypts files encrypted to fixed age1tag1... |