mirror of
https://github.com/FiloSottile/age.git
synced 2026-09-04 07:07:16 +00:00
age,cmd/age: reject oversized key files
Reported by Joe Doyle of Trail of Bits.
This commit is contained in:
+53
@@ -314,6 +314,59 @@ func TestParseErrorsDoNotIncludeLine(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func paddedFile(pad int, lines string) io.Reader {
|
||||
const chunkSize = 1 << 16
|
||||
chunk := strings.Repeat("#\n", chunkSize/2)
|
||||
var readers []io.Reader
|
||||
if pad%2 != 0 {
|
||||
readers = append(readers, strings.NewReader("\n"))
|
||||
pad--
|
||||
}
|
||||
for ; pad > chunkSize; pad -= chunkSize {
|
||||
readers = append(readers, strings.NewReader(chunk))
|
||||
}
|
||||
return io.MultiReader(append(readers,
|
||||
strings.NewReader(chunk[:pad]), strings.NewReader(lines))...)
|
||||
}
|
||||
|
||||
func TestParseFileSizeLimit(t *testing.T) {
|
||||
const sizeLimit = 1 << 24
|
||||
const identity = "AGE-SECRET-KEY-1D6K0SGAX3NU66R4GYFZY0UQWCLM3UUSF3CXLW4KXZM342WQSJ82QKU59QJ\n"
|
||||
const recipient = "age1cy0su9fwf3gf9mw868g5yut09p6nytfmmnktexz2ya5uqg9vl9sss4euqm\n"
|
||||
tests := []struct {
|
||||
name string
|
||||
entry string
|
||||
parse func(io.Reader) (int, error)
|
||||
}{
|
||||
{"identities", identity, func(r io.Reader) (int, error) {
|
||||
ids, err := age.ParseIdentities(r)
|
||||
return len(ids), err
|
||||
}},
|
||||
{"recipients", recipient, func(r io.Reader) (int, error) {
|
||||
recipients, err := age.ParseRecipients(r)
|
||||
return len(recipients), err
|
||||
}},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name+"/at limit", func(t *testing.T) {
|
||||
r := paddedFile(sizeLimit-4*len(tt.entry), strings.Repeat(tt.entry, 4))
|
||||
n, err := tt.parse(r)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if n != 4 {
|
||||
t.Errorf("got %d entries, want 4", n)
|
||||
}
|
||||
})
|
||||
t.Run(tt.name+"/over limit", func(t *testing.T) {
|
||||
r := paddedFile(sizeLimit-4*len(tt.entry), strings.Repeat(tt.entry, 8))
|
||||
if _, err := tt.parse(r); err == nil {
|
||||
t.Error("expected an error")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type testRecipient struct {
|
||||
labels []string
|
||||
}
|
||||
|
||||
@@ -5,9 +5,11 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
@@ -51,6 +53,70 @@ func (testPlugin) Unwrap(ss []*age.Stanza) ([]byte, error) {
|
||||
return nil, age.ErrIncorrectIdentity
|
||||
}
|
||||
|
||||
func paddedFile(pad int, lines string) io.Reader {
|
||||
const chunkSize = 1 << 16
|
||||
chunk := strings.Repeat("#\n", chunkSize/2)
|
||||
var readers []io.Reader
|
||||
if pad%2 != 0 {
|
||||
readers = append(readers, strings.NewReader("\n"))
|
||||
pad--
|
||||
}
|
||||
for ; pad > chunkSize; pad -= chunkSize {
|
||||
readers = append(readers, strings.NewReader(chunk))
|
||||
}
|
||||
return io.MultiReader(append(readers,
|
||||
strings.NewReader(chunk[:pad]), strings.NewReader(lines))...)
|
||||
}
|
||||
|
||||
func TestParseFileSizeLimit(t *testing.T) {
|
||||
const sizeLimit = 16 << 20
|
||||
const identity = "AGE-SECRET-KEY-1D6K0SGAX3NU66R4GYFZY0UQWCLM3UUSF3CXLW4KXZM342WQSJ82QKU59QJ\n"
|
||||
const recipient = "age1cy0su9fwf3gf9mw868g5yut09p6nytfmmnktexz2ya5uqg9vl9sss4euqm\n"
|
||||
tests := []struct {
|
||||
name string
|
||||
entry string
|
||||
parse func(*testing.T, io.Reader) (int, error)
|
||||
}{
|
||||
{"identities", identity, func(_ *testing.T, r io.Reader) (int, error) {
|
||||
ids, err := parseIdentities(r)
|
||||
return len(ids), err
|
||||
}},
|
||||
{"recipients", recipient, func(t *testing.T, r io.Reader) (int, error) {
|
||||
name := filepath.Join(t.TempDir(), "recipients.txt")
|
||||
f, err := os.Create(name)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := io.Copy(f, r); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := f.Close(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
recipients, err := parseRecipientsFile(name)
|
||||
return len(recipients), err
|
||||
}},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name+"/at limit", func(t *testing.T) {
|
||||
r := paddedFile(sizeLimit-4*len(tt.entry), strings.Repeat(tt.entry, 4))
|
||||
n, err := tt.parse(t, r)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if n != 4 {
|
||||
t.Errorf("got %d entries, want 4", n)
|
||||
}
|
||||
})
|
||||
t.Run(tt.name+"/over limit", func(t *testing.T) {
|
||||
r := paddedFile(sizeLimit-4*len(tt.entry), strings.Repeat(tt.entry, 8))
|
||||
if _, err := tt.parse(t, r); err == nil {
|
||||
t.Error("expected an error")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
var buildExtraCommands = sync.OnceValue(func() error {
|
||||
bindir := filepath.SplitList(os.Getenv("PATH"))[0]
|
||||
// Build age-keygen and age-plugin-pq into the test binary directory.
|
||||
|
||||
+10
-2
@@ -71,7 +71,8 @@ func parseRecipientsFile(name string) ([]age.Recipient, error) {
|
||||
const recipientFileSizeLimit = 16 << 20 // 16 MiB
|
||||
const lineLengthLimit = 8 << 10 // 8 KiB, same as sshd(8)
|
||||
var recs []age.Recipient
|
||||
scanner := bufio.NewScanner(io.LimitReader(f, recipientFileSizeLimit))
|
||||
lr := &io.LimitedReader{R: f, N: recipientFileSizeLimit + 1}
|
||||
scanner := bufio.NewScanner(lr)
|
||||
var n int
|
||||
for scanner.Scan() {
|
||||
n++
|
||||
@@ -104,6 +105,9 @@ func parseRecipientsFile(name string) ([]age.Recipient, error) {
|
||||
if err := scanner.Err(); err != nil {
|
||||
return nil, fmt.Errorf("%q: failed to read recipients file: %v", name, err)
|
||||
}
|
||||
if lr.N == 0 {
|
||||
return nil, fmt.Errorf("%q: recipients file is too long", name)
|
||||
}
|
||||
if len(recs) == 0 {
|
||||
return nil, fmt.Errorf("%q: no recipients found", name)
|
||||
}
|
||||
@@ -226,7 +230,8 @@ func parseIdentity(s string) (age.Identity, error) {
|
||||
func parseIdentities(f io.Reader) ([]age.Identity, error) {
|
||||
const privateKeySizeLimit = 1 << 24 // 16 MiB
|
||||
var ids []age.Identity
|
||||
scanner := bufio.NewScanner(io.LimitReader(f, privateKeySizeLimit))
|
||||
lr := &io.LimitedReader{R: f, N: privateKeySizeLimit + 1}
|
||||
scanner := bufio.NewScanner(lr)
|
||||
var n int
|
||||
for scanner.Scan() {
|
||||
n++
|
||||
@@ -249,6 +254,9 @@ func parseIdentities(f io.Reader) ([]age.Identity, error) {
|
||||
if err := scanner.Err(); err != nil {
|
||||
return nil, fmt.Errorf("failed to read identities file: %v", err)
|
||||
}
|
||||
if lr.N == 0 {
|
||||
return nil, fmt.Errorf("identities file is too long")
|
||||
}
|
||||
if len(ids) == 0 {
|
||||
return nil, fmt.Errorf("no identities found")
|
||||
}
|
||||
|
||||
@@ -24,7 +24,8 @@ import (
|
||||
func ParseIdentities(f io.Reader) ([]Identity, error) {
|
||||
const privateKeySizeLimit = 1 << 24 // 16 MiB
|
||||
var ids []Identity
|
||||
scanner := bufio.NewScanner(io.LimitReader(f, privateKeySizeLimit))
|
||||
lr := &io.LimitedReader{R: f, N: privateKeySizeLimit + 1}
|
||||
scanner := bufio.NewScanner(lr)
|
||||
var n int
|
||||
for scanner.Scan() {
|
||||
n++
|
||||
@@ -44,6 +45,9 @@ func ParseIdentities(f io.Reader) ([]Identity, error) {
|
||||
if err := scanner.Err(); err != nil {
|
||||
return nil, fmt.Errorf("failed to read identities file: %v", err)
|
||||
}
|
||||
if lr.N == 0 {
|
||||
return nil, fmt.Errorf("identities file is too long")
|
||||
}
|
||||
if len(ids) == 0 {
|
||||
return nil, fmt.Errorf("no identities found")
|
||||
}
|
||||
@@ -76,7 +80,8 @@ func parseIdentity(arg string) (Identity, error) {
|
||||
func ParseRecipients(f io.Reader) ([]Recipient, error) {
|
||||
const recipientFileSizeLimit = 1 << 24 // 16 MiB
|
||||
var recs []Recipient
|
||||
scanner := bufio.NewScanner(io.LimitReader(f, recipientFileSizeLimit))
|
||||
lr := &io.LimitedReader{R: f, N: recipientFileSizeLimit + 1}
|
||||
scanner := bufio.NewScanner(lr)
|
||||
var n int
|
||||
for scanner.Scan() {
|
||||
n++
|
||||
@@ -96,6 +101,9 @@ func ParseRecipients(f io.Reader) ([]Recipient, error) {
|
||||
if err := scanner.Err(); err != nil {
|
||||
return nil, fmt.Errorf("failed to read recipients file: %v", err)
|
||||
}
|
||||
if lr.N == 0 {
|
||||
return nil, fmt.Errorf("recipients file is too long")
|
||||
}
|
||||
if len(recs) == 0 {
|
||||
return nil, fmt.Errorf("no recipients found")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user