mirror of
https://github.com/FiloSottile/age.git
synced 2026-11-08 12:26:58 +00:00
Compare commits
15
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b662811909 | ||
|
|
5f603b5057 | ||
|
|
a68b8e921b | ||
|
|
706dfc1e79 | ||
|
|
4c86929a84 | ||
|
|
db09cd66d1 | ||
|
|
4a3a4ef00a | ||
|
|
10561a774f | ||
|
|
c918a2c113 | ||
|
|
beb5dfa28c | ||
|
|
0293aca1d7 | ||
|
|
e7601d8a67 | ||
|
|
acfa73142b | ||
|
|
b8564adb6d | ||
|
|
e4c611f778 |
No files matched your search
@@ -17,16 +17,17 @@ jobs:
|
||||
- {GOOS: linux, GOARCH: arm, GOARM: 6}
|
||||
- {GOOS: linux, GOARCH: arm64}
|
||||
- {GOOS: darwin, GOARCH: arm64}
|
||||
- {GOOS: darwin, GOARCH: amd64}
|
||||
- {GOOS: windows, GOARCH: amd64}
|
||||
- {GOOS: freebsd, GOARCH: amd64}
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v6
|
||||
uses: actions/setup-go@v7
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
cache: false
|
||||
@@ -61,7 +62,7 @@ jobs:
|
||||
GOARCH: ${{ matrix.GOARCH }}
|
||||
GOARM: ${{ matrix.GOARM }}
|
||||
- name: Upload workflow artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: age-artifacts-${{ matrix.GOOS }}-${{ matrix.GOARCH }}
|
||||
path: age-*
|
||||
@@ -70,12 +71,12 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v6
|
||||
uses: actions/setup-go@v7
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
cache: false
|
||||
@@ -86,9 +87,14 @@ jobs:
|
||||
mkdir "$DIR/age"
|
||||
git archive --format=tar.gz HEAD | tar -xz -C "$DIR/age"
|
||||
( cd "$DIR/age"; go mod vendor )
|
||||
for cmd in "$DIR"/age/{cmd,extra}/*; do
|
||||
echo "package main" >> "$cmd/version.go"
|
||||
echo "" >> "$cmd/version.go"
|
||||
echo "func init() { Version = \"$VERSION\" }" >> "$cmd/version.go"
|
||||
done
|
||||
tar -cvzf "age-$VERSION-source.tar.gz" -C "$DIR" age
|
||||
- name: Upload workflow artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: age-artifacts-source
|
||||
path: age-*-source.tar.gz
|
||||
@@ -103,12 +109,12 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Download workflow artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
pattern: age-artifacts-*
|
||||
merge-multiple: true
|
||||
- name: Generate artifacts attestation
|
||||
uses: actions/attest-build-provenance@v3
|
||||
uses: actions/attest-build-provenance@v4
|
||||
with:
|
||||
subject-path: age-*
|
||||
- name: Upload release artifacts
|
||||
|
||||
@@ -13,10 +13,10 @@ jobs:
|
||||
name: Ronn
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v7
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: geomys/sandboxed-step@v1.2.1
|
||||
- uses: geomys/sandboxed-step@v1.2.2
|
||||
with:
|
||||
persist-workspace-changes: true
|
||||
run: |
|
||||
@@ -29,7 +29,7 @@ jobs:
|
||||
awk '/Filippo Valsorda/ { $0 = "<p>Filippo Valsorda <a href=\"mailto:age@filippo.io\" data-bare-link=\"true\">age@filippo.io</a></p>" } { print }' "$f" > "$f.tmp"
|
||||
mv "$f.tmp" "$f"
|
||||
done
|
||||
- uses: actions/upload-artifact@v4
|
||||
- uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: man-pages
|
||||
path: |
|
||||
@@ -42,10 +42,10 @@ jobs:
|
||||
contents: write
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v7
|
||||
with:
|
||||
persist-credentials: true
|
||||
- uses: actions/download-artifact@v4
|
||||
- uses: actions/download-artifact@v8
|
||||
with:
|
||||
name: man-pages
|
||||
path: doc/
|
||||
|
||||
+11
-11
@@ -21,10 +21,10 @@ jobs:
|
||||
- windows-latest
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v7
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: actions/setup-go@v6
|
||||
- uses: actions/setup-go@v7
|
||||
with:
|
||||
go-version: ${{ matrix.go.go-version }}
|
||||
- run: |
|
||||
@@ -38,13 +38,13 @@ jobs:
|
||||
- { go-version: stable }
|
||||
- { go-version: oldstable }
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v7
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: actions/setup-go@v6
|
||||
- uses: actions/setup-go@v7
|
||||
with:
|
||||
go-version: ${{ matrix.go.go-version }}
|
||||
- uses: geomys/sandboxed-step@v1.2.1
|
||||
- uses: geomys/sandboxed-step@v1.2.2
|
||||
with:
|
||||
run: |
|
||||
go get -u -t ./...
|
||||
@@ -52,24 +52,24 @@ jobs:
|
||||
staticcheck:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v7
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: actions/setup-go@v6
|
||||
- uses: actions/setup-go@v7
|
||||
with:
|
||||
go-version: stable
|
||||
- uses: geomys/sandboxed-step@v1.2.1
|
||||
- uses: geomys/sandboxed-step@v1.2.2
|
||||
with:
|
||||
run: go run honnef.co/go/tools/cmd/staticcheck@latest ./...
|
||||
govulncheck:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v7
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: actions/setup-go@v6
|
||||
- uses: actions/setup-go@v7
|
||||
with:
|
||||
go-version: stable
|
||||
- uses: geomys/sandboxed-step@v1.2.1
|
||||
- uses: geomys/sandboxed-step@v1.2.2
|
||||
with:
|
||||
run: go run golang.org/x/vuln/cmd/govulncheck@latest ./...
|
||||
@@ -7,6 +7,7 @@
|
||||
</p>
|
||||
|
||||
[](https://pkg.go.dev/filippo.io/age)
|
||||
[](https://sourcespotter.com/deps/?firstparty=filippo.io&package=filippo.io%2Fage)
|
||||
[-man%20page-lightgrey>)](https://filippo.io/age/age.1)
|
||||
[](https://age-encryption.org/v1)
|
||||
|
||||
@@ -151,7 +152,7 @@ On Windows, Linux, macOS, and FreeBSD you can use the pre-built binaries.
|
||||
|
||||
```
|
||||
https://dl.filippo.io/age/latest?for=linux/amd64
|
||||
https://dl.filippo.io/age/v1.3.0?for=darwin/arm64
|
||||
https://dl.filippo.io/age/v1.3.1?for=darwin/arm64
|
||||
...
|
||||
```
|
||||
|
||||
|
||||
@@ -11,13 +11,33 @@ ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIM1WpnEswJLPzvXJDiswowy48U+G+G1kmgwUE2eaRHZG
|
||||
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAz2WM5CyPLqiNjk7CLl4roDXwKhQ0QExXLebukZEZFS
|
||||
EOF
|
||||
|
||||
curl -JLO "https://dl.filippo.io/age/v1.3.0?for=darwin/arm64"
|
||||
curl -JLO "https://dl.filippo.io/age/v1.3.0?for=darwin/arm64&proof"
|
||||
curl -JLO "https://dl.filippo.io/age/v1.3.1?for=darwin/arm64"
|
||||
curl -JLO "https://dl.filippo.io/age/v1.3.1?for=darwin/arm64&proof"
|
||||
|
||||
go install sigsum.org/sigsum-go/cmd/sigsum-verify@v0.13.1
|
||||
sigsum-verify -k age-sigsum-key.pub -P sigsum-generic-2025-1 \
|
||||
age-v1.3.0-darwin-arm64.tar.gz.proof < age-v1.3.0-darwin-arm64.tar.gz
|
||||
age-v1.3.1-darwin-arm64.tar.gz.proof < age-v1.3.1-darwin-arm64.tar.gz
|
||||
```
|
||||
|
||||
You can learn more about what's happening above in the [Sigsum
|
||||
docs](https://www.sigsum.org/getting-started/).
|
||||
|
||||
### Release playbook
|
||||
|
||||
Dear future me, to sign a new release and produce Sigsum proofs, run the following
|
||||
|
||||
```
|
||||
VERSION=v1.3.1
|
||||
go install sigsum.org/sigsum-go/cmd/sigsum-verify@latest
|
||||
go install github.com/tillitis/tkey-ssh-agent/cmd/tkey-ssh-agent@main
|
||||
tkey-ssh-agent --agent-socket tkey-ssh-agent.sock --uss
|
||||
passage -c other/tkey-ssh-sigsum-age
|
||||
SSH_AUTH_SOCK=tkey-ssh-agent.sock ssh-add -L > tkey-ssh-agent.pub
|
||||
passage other/sigsum-ratelimit > sigsum-ratelimit
|
||||
gh release download $VERSION --repo FiloSottile/age --dir artifacts/
|
||||
SSH_AUTH_SOCK=tkey-ssh-agent.sock sigsum-submit -k tkey-ssh-agent.pub -P sigsum-generic-2025-1 -a sigsum-ratelimit -d filippo.io artifacts/*
|
||||
gh release upload $VERSION --repo FiloSottile/age artifacts/*.proof
|
||||
```
|
||||
|
||||
In the future, we will move to reproducing the artifacts locally, and signing
|
||||
those instead of the ones built by GitHub Actions.
|
||||
@@ -130,7 +130,7 @@ func encryptHdr(fileKey []byte, recipients ...Recipient) (*format.Header, error)
|
||||
sort.Strings(l)
|
||||
if i == 0 {
|
||||
labels = l
|
||||
} else if !slicesEqual(labels, l) {
|
||||
} else if !slices.Equal(labels, l) {
|
||||
return nil, incompatibleLabelsError(labels, l)
|
||||
}
|
||||
for _, s := range stanzas {
|
||||
@@ -208,18 +208,6 @@ func wrapWithLabels(r Recipient, fileKey []byte) (s []*Stanza, labels []string,
|
||||
return
|
||||
}
|
||||
|
||||
func slicesEqual(s1, s2 []string) bool {
|
||||
if len(s1) != len(s2) {
|
||||
return false
|
||||
}
|
||||
for i := range s1 {
|
||||
if s1[i] != s2[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func incompatibleLabelsError(l1, l2 []string) error {
|
||||
hasPQ1 := slices.Contains(l1, "postquantum")
|
||||
hasPQ2 := slices.Contains(l2, "postquantum")
|
||||
|
||||
@@ -24,6 +24,10 @@ Options:
|
||||
INPUT defaults to standard input. "-" may be used as INPUT to explicitly
|
||||
read from standard input.`
|
||||
|
||||
// Version can be set at link time to override debug.BuildInfo.Main.Version when
|
||||
// building manually without git history. It should look like "v1.2.3".
|
||||
var Version string
|
||||
|
||||
func main() {
|
||||
flag.Usage = func() { fmt.Fprintf(os.Stderr, "%s\n", usage) }
|
||||
|
||||
@@ -37,11 +41,10 @@ func main() {
|
||||
flag.Parse()
|
||||
|
||||
if versionFlag {
|
||||
if buildInfo, ok := debug.ReadBuildInfo(); ok {
|
||||
fmt.Println(buildInfo.Main.Version)
|
||||
return
|
||||
if buildInfo, ok := debug.ReadBuildInfo(); ok && Version == "" {
|
||||
Version = buildInfo.Main.Version
|
||||
}
|
||||
fmt.Println("(unknown)")
|
||||
fmt.Println(Version)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -56,6 +56,10 @@ Examples:
|
||||
$ age-keygen -y key.txt
|
||||
age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p`
|
||||
|
||||
// Version can be set at link time to override debug.BuildInfo.Main.Version when
|
||||
// building manually without git history. It should look like "v1.2.3".
|
||||
var Version string
|
||||
|
||||
func main() {
|
||||
log.SetFlags(0)
|
||||
flag.Usage = func() { fmt.Fprintf(os.Stderr, "%s\n", usage) }
|
||||
@@ -69,6 +73,15 @@ func main() {
|
||||
flag.StringVar(&outFlag, "o", "", "output to `FILE` (default stdout)")
|
||||
flag.StringVar(&outFlag, "output", "", "output to `FILE` (default stdout)")
|
||||
flag.Parse()
|
||||
|
||||
if versionFlag {
|
||||
if buildInfo, ok := debug.ReadBuildInfo(); ok && Version == "" {
|
||||
Version = buildInfo.Main.Version
|
||||
}
|
||||
fmt.Println(Version)
|
||||
return
|
||||
}
|
||||
|
||||
if len(flag.Args()) != 0 && !convertFlag {
|
||||
errorf("too many arguments")
|
||||
}
|
||||
@@ -78,14 +91,6 @@ func main() {
|
||||
if pqFlag && convertFlag {
|
||||
errorf("-pq cannot be used with -y")
|
||||
}
|
||||
if versionFlag {
|
||||
if buildInfo, ok := debug.ReadBuildInfo(); ok {
|
||||
fmt.Println(buildInfo.Main.Version)
|
||||
return
|
||||
}
|
||||
fmt.Println("(unknown)")
|
||||
return
|
||||
}
|
||||
|
||||
out := os.Stdout
|
||||
if outFlag != "" {
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"runtime/debug"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
@@ -73,6 +74,10 @@ When decrypting, you can set AGE_PASSPHRASE_MAX_WORK_FACTOR to limit the
|
||||
maximum scrypt work factor accepted (between 1 and 30, default 30). This can
|
||||
be used to avoid very slow decryptions.`
|
||||
|
||||
// Version can be set at link time to override debug.BuildInfo.Main.Version when
|
||||
// building manually without git history. It should look like "v1.2.3".
|
||||
var Version string
|
||||
|
||||
func main() {
|
||||
flag.Usage = func() { fmt.Fprintf(os.Stderr, "%s\n", usage) }
|
||||
|
||||
@@ -80,6 +85,19 @@ func main() {
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
p.RegisterFlags(nil)
|
||||
|
||||
versionFlag := flag.Bool("version", false, "print the version")
|
||||
flag.Parse()
|
||||
|
||||
if *versionFlag {
|
||||
if buildInfo, ok := debug.ReadBuildInfo(); ok && Version == "" {
|
||||
Version = buildInfo.Main.Version
|
||||
}
|
||||
fmt.Println(Version)
|
||||
return
|
||||
}
|
||||
|
||||
p.HandleIdentityAsRecipient(func(data []byte) (age.Recipient, error) {
|
||||
if len(data) != 0 {
|
||||
return nil, fmt.Errorf("batchpass identity does not take any payload")
|
||||
|
||||
+7
-6
@@ -98,6 +98,10 @@ func (f *identityFlags) addPluginFlag(value string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Version can be set at link time to override debug.BuildInfo.Main.Version when
|
||||
// building manually without git history. It should look like "v1.2.3".
|
||||
var Version string
|
||||
|
||||
func main() {
|
||||
flag.Usage = func() { fmt.Fprintf(os.Stderr, "%s\n", usage) }
|
||||
|
||||
@@ -136,13 +140,10 @@ func main() {
|
||||
flag.Parse()
|
||||
|
||||
if versionFlag {
|
||||
if buildInfo, ok := debug.ReadBuildInfo(); ok {
|
||||
// TODO: use buildInfo.Settings to prepare a pseudoversion such as
|
||||
// v0.0.0-20210817164053-32db794688a5+dirty on Go 1.18+.
|
||||
fmt.Println(buildInfo.Main.Version)
|
||||
return
|
||||
if buildInfo, ok := debug.ReadBuildInfo(); ok && Version == "" {
|
||||
Version = buildInfo.Main.Version
|
||||
}
|
||||
fmt.Println("(unknown)")
|
||||
fmt.Println(Version)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
.\" generated with Ronn-NG/v0.9.1
|
||||
.\" http://github.com/apjanke/ronn-ng/tree/0.9.1
|
||||
.TH "AGE\-INSPECT" "1" "December 2025" ""
|
||||
.TH "AGE\-INSPECT" "1" "August 2026" ""
|
||||
.SH "NAME"
|
||||
\fBage\-inspect\fR \- inspect age(1) encrypted files
|
||||
.SH "SYNOPSIS"
|
||||
|
||||
@@ -195,7 +195,7 @@ Tip: for machine-readable output, use --json.
|
||||
|
||||
<ol class='man-decor man-foot man foot'>
|
||||
<li class='tl'></li>
|
||||
<li class='tc'>December 2025</li>
|
||||
<li class='tc'>August 2026</li>
|
||||
<li class='tr'>age-inspect(1)</li>
|
||||
</ol>
|
||||
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
.\" generated with Ronn-NG/v0.9.1
|
||||
.\" http://github.com/apjanke/ronn-ng/tree/0.9.1
|
||||
.TH "AGE\-KEYGEN" "1" "December 2025" ""
|
||||
.TH "AGE\-KEYGEN" "1" "August 2026" ""
|
||||
.SH "NAME"
|
||||
\fBage\-keygen\fR \- generate age(1) key pairs
|
||||
.SH "SYNOPSIS"
|
||||
@@ -53,7 +53,7 @@ AGE\-SECRET\-KEY\-1N9JEPW6DWJ0ZQUDX63F5A03GX8QUW7PXDE39N8UYF82VZ9PC8UFS3M7XA9
|
||||
Write a new post\-quantum identity to \fBkey\.txt\fR:
|
||||
.IP "" 4
|
||||
.nf
|
||||
$ age\-keygen \-o key\.txt
|
||||
$ age\-keygen \-pq \-o key\.txt
|
||||
Public key: age1pq1cd[\|\.\|\.\|\. 1950 more characters \|\.\|\.\|\.]
|
||||
.fi
|
||||
.IP "" 0
|
||||
|
||||
@@ -130,7 +130,7 @@ AGE-SECRET-KEY-1N9JEPW6DWJ0ZQUDX63F5A03GX8QUW7PXDE39N8UYF82VZ9PC8UFS3M7XA9
|
||||
|
||||
<p>Write a new post-quantum identity to <code>key.txt</code>:</p>
|
||||
|
||||
<pre><code>$ age-keygen -o key.txt
|
||||
<pre><code>$ age-keygen -pq -o key.txt
|
||||
Public key: age1pq1cd[... 1950 more characters ...]
|
||||
</code></pre>
|
||||
|
||||
@@ -150,7 +150,7 @@ age1pq1cd[... 1950 more characters ...]
|
||||
|
||||
<ol class='man-decor man-foot man foot'>
|
||||
<li class='tl'></li>
|
||||
<li class='tc'>December 2025</li>
|
||||
<li class='tc'>August 2026</li>
|
||||
<li class='tr'>age-keygen(1)</li>
|
||||
</ol>
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ Generate a new traditional identity:
|
||||
|
||||
Write a new post-quantum identity to `key.txt`:
|
||||
|
||||
$ age-keygen -o key.txt
|
||||
$ age-keygen -pq -o key.txt
|
||||
Public key: age1pq1cd[... 1950 more characters ...]
|
||||
|
||||
Convert an identity to a recipient:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" generated with Ronn-NG/v0.9.1
|
||||
.\" http://github.com/apjanke/ronn-ng/tree/0.9.1
|
||||
.TH "AGE\-PLUGIN\-BATCHPASS" "1" "December 2025" ""
|
||||
.TH "AGE\-PLUGIN\-BATCHPASS" "1" "August 2026" ""
|
||||
.SH "NAME"
|
||||
\fBage\-plugin\-batchpass\fR \- non\-interactive passphrase encryption plugin for age(1)
|
||||
.SH "SYNOPSIS"
|
||||
|
||||
@@ -174,7 +174,7 @@ persisted in the shell history or leaked to other users on multi-user systems.</
|
||||
|
||||
<ol class='man-decor man-foot man foot'>
|
||||
<li class='tl'></li>
|
||||
<li class='tc'>December 2025</li>
|
||||
<li class='tc'>August 2026</li>
|
||||
<li class='tr'>age-plugin-batchpass(1)</li>
|
||||
</ol>
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" generated with Ronn-NG/v0.9.1
|
||||
.\" http://github.com/apjanke/ronn-ng/tree/0.9.1
|
||||
.TH "AGE" "1" "December 2025" ""
|
||||
.TH "AGE" "1" "August 2026" ""
|
||||
.SH "NAME"
|
||||
\fBage\fR \- simple, modern, and secure file encryption
|
||||
.SH "SYNOPSIS"
|
||||
|
||||
+1
-1
@@ -461,7 +461,7 @@ $ age -d -i age-yubikey-identity-388178f3.txt secrets.txt.age
|
||||
|
||||
<ol class='man-decor man-foot man foot'>
|
||||
<li class='tl'></li>
|
||||
<li class='tc'>December 2025</li>
|
||||
<li class='tc'>August 2026</li>
|
||||
<li class='tr'>age(1)</li>
|
||||
</ol>
|
||||
|
||||
|
||||
@@ -30,12 +30,16 @@ implementation of age that supports plugins.
|
||||
Recipients work out of the box, while identities need to be converted to plugin
|
||||
identities with -identity. If OUTPUT already exists, it is not overwritten.`
|
||||
|
||||
// Version can be set at link time to override debug.BuildInfo.Main.Version when
|
||||
// building manually without git history. It should look like "v1.2.3".
|
||||
var Version string
|
||||
|
||||
func main() {
|
||||
log.SetFlags(0)
|
||||
|
||||
p, err := plugin.New("pq")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
errorf("failed to create plugin: %v", err)
|
||||
}
|
||||
p.RegisterFlags(nil)
|
||||
|
||||
@@ -50,11 +54,10 @@ func main() {
|
||||
flag.Parse()
|
||||
|
||||
if versionFlag {
|
||||
if buildInfo, ok := debug.ReadBuildInfo(); ok {
|
||||
fmt.Println(buildInfo.Main.Version)
|
||||
return
|
||||
if buildInfo, ok := debug.ReadBuildInfo(); ok && Version == "" {
|
||||
Version = buildInfo.Main.Version
|
||||
}
|
||||
fmt.Println("(unknown)")
|
||||
fmt.Println(Version)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"runtime/debug"
|
||||
|
||||
"filippo.io/age"
|
||||
"filippo.io/age/plugin"
|
||||
@@ -18,6 +19,10 @@ support to any version and implementation of age that supports plugins.
|
||||
Usually, tagged recipients are the public side of private keys held in hardware,
|
||||
where the identity side is handled by a different plugin.`
|
||||
|
||||
// Version can be set at link time to override debug.BuildInfo.Main.Version when
|
||||
// building manually without git history. It should look like "v1.2.3".
|
||||
var Version string
|
||||
|
||||
func main() {
|
||||
flag.Usage = func() { fmt.Fprintf(os.Stderr, "%s\n", usage) }
|
||||
|
||||
@@ -25,8 +30,22 @@ func main() {
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
p.RegisterFlags(nil)
|
||||
|
||||
versionFlag := flag.Bool("version", false, "print the version")
|
||||
flag.Parse()
|
||||
|
||||
if *versionFlag {
|
||||
if buildInfo, ok := debug.ReadBuildInfo(); ok && Version == "" {
|
||||
Version = buildInfo.Main.Version
|
||||
}
|
||||
fmt.Println(Version)
|
||||
return
|
||||
}
|
||||
|
||||
p.HandleRecipient(func(b []byte) (age.Recipient, error) {
|
||||
return tag.NewClassicRecipient(b)
|
||||
})
|
||||
|
||||
os.Exit(p.Main())
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"runtime/debug"
|
||||
|
||||
"filippo.io/age"
|
||||
"filippo.io/age/plugin"
|
||||
@@ -19,6 +20,10 @@ implementation of age that supports plugins.
|
||||
Usually, tagged recipients are the public side of private keys held in hardware,
|
||||
where the identity side is handled by a different plugin.`
|
||||
|
||||
// Version can be set at link time to override debug.BuildInfo.Main.Version when
|
||||
// building manually without git history. It should look like "v1.2.3".
|
||||
var Version string
|
||||
|
||||
func main() {
|
||||
flag.Usage = func() { fmt.Fprintf(os.Stderr, "%s\n", usage) }
|
||||
|
||||
@@ -26,8 +31,22 @@ func main() {
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
p.RegisterFlags(nil)
|
||||
|
||||
versionFlag := flag.Bool("version", false, "print the version")
|
||||
flag.Parse()
|
||||
|
||||
if *versionFlag {
|
||||
if buildInfo, ok := debug.ReadBuildInfo(); ok && Version == "" {
|
||||
Version = buildInfo.Main.Version
|
||||
}
|
||||
fmt.Println(Version)
|
||||
return
|
||||
}
|
||||
|
||||
p.HandleRecipient(func(b []byte) (age.Recipient, error) {
|
||||
return tag.NewHybridRecipient(b)
|
||||
})
|
||||
|
||||
os.Exit(p.Main())
|
||||
}
|
||||
@@ -1,22 +1,22 @@
|
||||
module filippo.io/age
|
||||
|
||||
go 1.24.0
|
||||
go 1.25.0
|
||||
|
||||
// Release build version.
|
||||
toolchain go1.25.5
|
||||
toolchain go1.27.0
|
||||
|
||||
require (
|
||||
filippo.io/edwards25519 v1.1.0
|
||||
filippo.io/edwards25519 v1.2.0
|
||||
filippo.io/hpke v0.4.0
|
||||
filippo.io/nistec v0.0.4
|
||||
golang.org/x/crypto v0.45.0
|
||||
golang.org/x/sys v0.38.0
|
||||
golang.org/x/term v0.37.0
|
||||
golang.org/x/crypto v0.55.0
|
||||
golang.org/x/sys v0.47.0
|
||||
golang.org/x/term v0.45.0
|
||||
)
|
||||
|
||||
// Test dependencies.
|
||||
require (
|
||||
c2sp.org/CCTV/age v0.0.0-20251208015420-e9274a7bdbfd
|
||||
github.com/rogpeppe/go-internal v1.14.1
|
||||
golang.org/x/tools v0.39.0 // indirect
|
||||
c2sp.org/CCTV/age v0.0.0-20260605191406-1e3d2860d46e
|
||||
github.com/rogpeppe/go-internal v1.16.0
|
||||
golang.org/x/tools v0.49.0 // indirect
|
||||
)
|
||||
@@ -1,18 +1,18 @@
|
||||
c2sp.org/CCTV/age v0.0.0-20251208015420-e9274a7bdbfd h1:ZLsPO6WdZ5zatV4UfVpr7oAwLGRZ+sebTUruuM4Ra3M=
|
||||
c2sp.org/CCTV/age v0.0.0-20251208015420-e9274a7bdbfd/go.mod h1:SrHC2C7r5GkDk8R+NFVzYy/sdj0Ypg9htaPXQq5Cqeo=
|
||||
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
||||
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||
c2sp.org/CCTV/age v0.0.0-20260605191406-1e3d2860d46e h1:N9DpijOhcuCLIlgcX/6qhBQeMQIlBxsVQITjbpJaBso=
|
||||
c2sp.org/CCTV/age v0.0.0-20260605191406-1e3d2860d46e/go.mod h1:SrHC2C7r5GkDk8R+NFVzYy/sdj0Ypg9htaPXQq5Cqeo=
|
||||
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/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
||||
golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
|
||||
golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
|
||||
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
|
||||
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU=
|
||||
golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254=
|
||||
golang.org/x/tools v0.39.0 h1:ik4ho21kwuQln40uelmciQPp9SipgNDdrafrYA4TmQQ=
|
||||
golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ=
|
||||
github.com/rogpeppe/go-internal v1.16.0 h1:O9DK+vNMDVGLr2BeZqmpLeMjiMNkuXfcqntWbZV6S5g=
|
||||
github.com/rogpeppe/go-internal v1.16.0/go.mod h1:DrUVZyrJU+txYW5/1kwtXQSMFio52ZOxX7yM1VHvnxs=
|
||||
golang.org/x/crypto v0.55.0 h1:+KWHjbgOaAQ66dh/YlkZKHlz9ZUlq61AFirAR9ntP8M=
|
||||
golang.org/x/crypto v0.55.0/go.mod h1:uq0V9dE/fzQuJtbnL+2EhWOE63vo164FY8xqEnV9xis=
|
||||
golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs=
|
||||
golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/term v0.45.0 h1:NwWyBmoJCbfTHpxrWoZ9C6/VxOf7ic219I8xZZFdrf0=
|
||||
golang.org/x/term v0.45.0/go.mod h1:9aqxs0blBcrm/n0L9QW0aRVD+ktan8ssZromtqJC43w=
|
||||
golang.org/x/tools v0.49.0 h1:3NI7VXzL9+1WZD52Dx2ttoPwD5DWrFGpl9mFZDlmisI=
|
||||
golang.org/x/tools v0.49.0/go.mod h1:SJNXV9DBKT0UbdttsQjbfJlAE/q+y36++zo3uL3N0Oo=
|
||||
@@ -76,6 +76,7 @@ func (w *WrappedBase64Encoder) writeWrapped(p []byte) (int, error) {
|
||||
if w.buf.Len() != 0 {
|
||||
panic("age: internal error: non-empty WrappedBase64Encoder.buf")
|
||||
}
|
||||
total := len(p)
|
||||
for len(p) > 0 {
|
||||
toWrite := min(ColumnsPerLine-(w.written%ColumnsPerLine), len(p))
|
||||
n, _ := w.buf.Write(p[:toWrite])
|
||||
@@ -91,7 +92,7 @@ func (w *WrappedBase64Encoder) writeWrapped(p []byte) (int, error) {
|
||||
// are not recoverable anyway.
|
||||
return 0, err
|
||||
}
|
||||
return len(p), nil
|
||||
return total, nil
|
||||
}
|
||||
|
||||
// LastLineIsEmpty returns whether the last output line was empty, either
|
||||
|
||||
@@ -21,7 +21,7 @@ type Metadata struct {
|
||||
Header int64 `json:"header"`
|
||||
Armor int64 `json:"armor"`
|
||||
Overhead int64 `json:"overhead"`
|
||||
// Currently, we don't do any padding, not MinPayload == MaxPayload and
|
||||
// Currently, we don't do any padding, so MinPayload == MaxPayload and
|
||||
// MinPadding == MaxPadding == 0, but that might change in the future.
|
||||
MinPayload int64 `json:"min_payload"`
|
||||
MaxPayload int64 `json:"max_payload"`
|
||||
@@ -61,9 +61,9 @@ func Inspect(r io.Reader, fileSize int64) (*Metadata, error) {
|
||||
for _, s := range hdr.Recipients {
|
||||
data.StanzaTypes = append(data.StanzaTypes, s.Type)
|
||||
switch s.Type {
|
||||
case "X25519", "ssh-rsa", "ssh-ed25519", "age-encryption.org/p256tag", "piv-p256":
|
||||
case "X25519", "ssh-rsa", "ssh-ed25519", "p256tag", "piv-p256":
|
||||
data.Postquantum = "no"
|
||||
case "mlkem768x25519", "scrypt", "age-encryption.org/mlkem768p256tag":
|
||||
case "mlkem768x25519", "scrypt", "mlkem768p256tag":
|
||||
if data.Postquantum != "no" {
|
||||
data.Postquantum = "yes"
|
||||
}
|
||||
|
||||
@@ -1,12 +1,57 @@
|
||||
package inspect
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"filippo.io/age/internal/format"
|
||||
"filippo.io/age/internal/stream"
|
||||
)
|
||||
|
||||
// buildFile serializes a header with a single stanza of the given type,
|
||||
// followed by the minimal valid encrypted payload (a 16-byte stream nonce
|
||||
// and a single empty ChaCha20-Poly1305 chunk).
|
||||
func buildFile(t *testing.T, stanzaType string) []byte {
|
||||
t.Helper()
|
||||
hdr := &format.Header{
|
||||
Recipients: []*format.Stanza{{Type: stanzaType}},
|
||||
MAC: make([]byte, 32),
|
||||
}
|
||||
buf := &bytes.Buffer{}
|
||||
if err := hdr.Marshal(buf); err != nil {
|
||||
t.Fatalf("Header.Marshal: %v", err)
|
||||
}
|
||||
// Append nonce (16 bytes) + poly1305 tag for empty chunk (16 bytes).
|
||||
buf.Write(make([]byte, 16+16))
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
func TestInspectTagStanzas(t *testing.T) {
|
||||
tests := []struct {
|
||||
stanzaType string
|
||||
want string
|
||||
}{
|
||||
{stanzaType: "p256tag", want: "no"},
|
||||
{stanzaType: "mlkem768p256tag", want: "yes"},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.stanzaType, func(t *testing.T) {
|
||||
f := buildFile(t, tt.stanzaType)
|
||||
md, err := Inspect(bytes.NewReader(f), int64(len(f)))
|
||||
if err != nil {
|
||||
t.Fatalf("Inspect: %v", err)
|
||||
}
|
||||
if got := md.Postquantum; got != tt.want {
|
||||
t.Errorf("Postquantum = %q, want %q", got, tt.want)
|
||||
}
|
||||
if len(md.StanzaTypes) != 1 || md.StanzaTypes[0] != tt.stanzaType {
|
||||
t.Errorf("StanzaTypes = %v, want [%q]", md.StanzaTypes, tt.stanzaType)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestStreamOverhead(t *testing.T) {
|
||||
tests := []struct {
|
||||
payloadSize int64
|
||||
|
||||
+3
-14
@@ -1,6 +1,6 @@
|
||||
// Package plugin implements the age plugin protocol.
|
||||
//
|
||||
// [Recipient] and [Indentity] are plugin clients, that execute plugin binaries to
|
||||
// [Recipient] and [Identity] are plugin clients, that execute plugin binaries to
|
||||
// perform encryption and decryption operations.
|
||||
//
|
||||
// [Plugin] is a framework for writing age plugins, that exposes an [age.Recipient]
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"slices"
|
||||
"strconv"
|
||||
|
||||
"filippo.io/age"
|
||||
@@ -358,7 +359,7 @@ func wrapWithLabels(r age.Recipient, fileKey []byte) ([]*age.Stanza, []string, e
|
||||
}
|
||||
|
||||
func checkLabels(ll, labels []string) error {
|
||||
if !slicesEqual(ll, labels) {
|
||||
if !slices.Equal(ll, labels) {
|
||||
return fmt.Errorf("labels %q do not match previous recipients %q", ll, labels)
|
||||
}
|
||||
return nil
|
||||
@@ -662,15 +663,3 @@ func (p *Plugin) writeError(args []string, err error) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func slicesEqual(s1, s2 []string) bool {
|
||||
if len(s1) != len(s2) {
|
||||
return false
|
||||
}
|
||||
for i := range s1 {
|
||||
if s1[i] != s2[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
Reference in new issue
Block a user